mpx/commonframework/common/src/mpxcollectionpath.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:23:05 +0100
branchRCL_3
changeset 56 63223d4fd956
parent 55 6c1dfe4da5dd
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* 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:  implementation of collection path
*
*/

#include <s32strm.h>
#include <e32panic.h>
#include <mpxuser.h>
#include <mpxcmn.h>
#include <mpxattribute.h>
#include <mpxdata.h>
#include "mpxcollectionpath.h"

/**
* Encapsulates single node in collection hierarchy level. A node represents a
* level of the collection path
*/
NONSHARABLE_CLASS(CMPXCollectionPathNode) : public CBase
    {
public:
    static CMPXCollectionPathNode* NewL();
    static CMPXCollectionPathNode* NewLC();
    static CMPXCollectionPathNode* NewLC(const CMPXCollectionPathNode& aNode);
    virtual ~CMPXCollectionPathNode();
public:
    /**
    * Sets current item at this level
    */
    void Set(const TMPXItemId& aId, TInt aIndex);

    /**
    * Sets the next level open mode
    */
    void Set(TMPXOpenMode aRequest);

    /**
    * Sets the next level open attributes
    */
    void SetL(const TArray<TMPXAttribute>& aAttrs);

    /**
    * Current index
    */
    TInt Index() const;

    /**
    * Current Id
    */
    const TMPXItemId& Id() const;

    /**
    * Open mode for next level
    */
    TMPXOpenMode OpenMode() const;

    /**
    * Open attributes for next level
    */
    const TArray<TMPXAttribute> OpenAttributes() const;

public:
    void ExternalizeL(RWriteStream& aStream) const;
    void InternalizeL(RReadStream& aStream);

private:
    CMPXCollectionPathNode();
    void ConstructL(const CMPXCollectionPathNode& aNode);
    void ConstructL();

private:
    TMPXItemId iId;
    TInt iIndex;
    TMPXOpenMode iOpenMode;
    RArray<TMPXAttribute> iAttrs; // Open attributes for next level
    };


// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::NewL
// -----------------------------------------------------------------------------
//
CMPXCollectionPathNode* CMPXCollectionPathNode::NewL()
    {
    CMPXCollectionPathNode* self = NewLC();
    CleanupStack::Pop(self);
    return self;
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::NewLC
// -----------------------------------------------------------------------------
//
CMPXCollectionPathNode* CMPXCollectionPathNode::NewLC()
    {
    CMPXCollectionPathNode* self = new(ELeave) CMPXCollectionPathNode();
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::NewLC
// -----------------------------------------------------------------------------
//
CMPXCollectionPathNode* CMPXCollectionPathNode::NewLC(
    const CMPXCollectionPathNode& aNode)
    {
    CMPXCollectionPathNode* self = new(ELeave) CMPXCollectionPathNode();
    CleanupStack::PushL(self);
    self->ConstructL(aNode);
    return self;
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::ConstructL
// -----------------------------------------------------------------------------
//
void CMPXCollectionPathNode::ConstructL()
    {
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::ConstructL
// -----------------------------------------------------------------------------
//
void CMPXCollectionPathNode::ConstructL(
    const CMPXCollectionPathNode& aNode)
    {
    iId=aNode.iId;
    iIndex=aNode.iIndex;
    iOpenMode=aNode.iOpenMode;
    ::CopyArrayL<TMPXAttribute>(aNode.iAttrs.Array(), iAttrs);
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::CMPXCollectionPathNode
// -----------------------------------------------------------------------------
//
CMPXCollectionPathNode::CMPXCollectionPathNode()
:   iId(KMPXInvalidItemId),
    iIndex(KErrNotFound),
    iOpenMode(EMPXOpenGroupOrPlaylist)
    {
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::~CMPXCollectionPathNode
// -----------------------------------------------------------------------------
//
CMPXCollectionPathNode::~CMPXCollectionPathNode()
    {
    iAttrs.Close();
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::Set
// -----------------------------------------------------------------------------
//
void CMPXCollectionPathNode::Set(const TMPXItemId& aId, TInt aIndex)
    {
    iId=aId;
    iIndex=aIndex;
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::Set
// -----------------------------------------------------------------------------
//
void CMPXCollectionPathNode::Set(TMPXOpenMode aMode)
    {
    iOpenMode=aMode;
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::Set
// -----------------------------------------------------------------------------
//
void CMPXCollectionPathNode::SetL(const TArray<TMPXAttribute>& aAttrs)
    {
    ::CopyArrayL(aAttrs, iAttrs);
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::Index
// -----------------------------------------------------------------------------
//
TInt CMPXCollectionPathNode::Index() const
    {
    return iIndex;
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::Id
// -----------------------------------------------------------------------------
//
const TMPXItemId& CMPXCollectionPathNode::Id() const
    {
    return iId;
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::OpenMode
// -----------------------------------------------------------------------------
//
TMPXOpenMode CMPXCollectionPathNode::OpenMode() const
    {
    return iOpenMode;
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPathNode::OpenAttributes
// -----------------------------------------------------------------------------
//
const TArray<TMPXAttribute> CMPXCollectionPathNode::OpenAttributes() const
    {
    return iAttrs.Array();
    }

// -----------------------------------------------------------------------------
// Externalize object
// -----------------------------------------------------------------------------
//
void CMPXCollectionPathNode::ExternalizeL(RWriteStream& aStream) const
    {
    aStream.WriteUint32L(iId.iId1);
    aStream.WriteUint32L(iId.iId2);
    aStream.WriteInt32L(iIndex);
    aStream.WriteInt32L(iOpenMode);
    ::ExternalizeL(iAttrs.Array(), aStream);
    }

// -----------------------------------------------------------------------------
// Internalize object
// -----------------------------------------------------------------------------
//
void CMPXCollectionPathNode::InternalizeL(RReadStream& aStream)
    {
    TUint32 id1 = aStream.ReadUint32L();
    TUint32 id2 = aStream.ReadUint32L();
    iId=TMPXItemId( id1, id2 );
    iIndex=aStream.ReadInt32L();
    iOpenMode=static_cast<TMPXOpenMode>(aStream.ReadInt32L());
    iAttrs.Reset();
    ::InternalizeL(iAttrs, aStream);
    }

// ============================== MEMBER FUNCTIONS =============================

// -----------------------------------------------------------------------------
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CMPXCollectionPath* CMPXCollectionPath::NewL()
    {
    CMPXCollectionPath* p = new(ELeave) CMPXCollectionPath();
    CleanupStack::PushL(p);
    p->ConstructL();
    CleanupStack::Pop(p);
    return p;
    }

// -----------------------------------------------------------------------------
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CMPXCollectionPath* CMPXCollectionPath::NewL(
    const CMPXCollectionPath& aPath)
    {
    CMPXCollectionPath* p = new(ELeave) CMPXCollectionPath();
    CleanupStack::PushL(p);
    p->ConstructL(aPath);
    CleanupStack::Pop(p);
    return p;
    }

// -----------------------------------------------------------------------------
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CMPXCollectionPath* CMPXCollectionPath::NewL(
    RReadStream& aStream)
    {
    CMPXCollectionPath* p = new(ELeave) CMPXCollectionPath();
    CleanupStack::PushL(p);
    p->ConstructL(aStream);
    CleanupStack::Pop(p);
    return p;
    }

// -----------------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------------
//
EXPORT_C CMPXCollectionPath::~CMPXCollectionPath()
    {
    iNodeArray.ResetAndDestroy();
    iNodeArray.Close();
    iIds.Close();
    iSelection.Close();
    }

// -----------------------------------------------------------------------------
// Default constructor
// -----------------------------------------------------------------------------
//
CMPXCollectionPath::CMPXCollectionPath() : iInvalidId(KMPXInvalidItemId)
    {
    }

// ----------------------------------------------------------------------------
// 2nd phase constructor.
// ----------------------------------------------------------------------------
//
void CMPXCollectionPath::ConstructL(const CMPXCollectionPath& aPath)
    {
    iNodeArray.ResetAndDestroy();
    TInt count = aPath.iNodeArray.Count();
    for (TInt i=0; i<count; ++i)
        {
        CMPXCollectionPathNode* node =
            CMPXCollectionPathNode::NewLC(*(aPath.iNodeArray[i]));
        iNodeArray.AppendL(node);
        CleanupStack::Pop(node);
        }
    ::CopyArrayL(aPath.iIds.Array(),iIds);
    ::CopyArrayL(aPath.iSelection.Array(),iSelection);
    }

// ----------------------------------------------------------------------------
// 2nd phase constructor.
// ----------------------------------------------------------------------------
//
void CMPXCollectionPath::ConstructL()
    {
    }

// ----------------------------------------------------------------------------
// 2nd phase constructor.
// ----------------------------------------------------------------------------
//
void CMPXCollectionPath::ConstructL(RReadStream& aStream)
    {
    InternalizeL(aStream);
    }

// -----------------------------------------------------------------------------
// Advances path to next item
// -----------------------------------------------------------------------------
//
EXPORT_C TBool CMPXCollectionPath::operator++()
    {
    TBool ret(EFalse);
    TInt index=Index();
    if (index < Count()-1)
        {
        Set(++index);
        ret=ETrue;
        } // else the last one, no more to go and return false
    return ret;
    }

// -----------------------------------------------------------------------------
// Advances path to previous item
// -----------------------------------------------------------------------------
//
EXPORT_C TBool CMPXCollectionPath::operator--()
    {
    TBool ret=EFalse;
    TInt index=Index();
    if (index>0)
        {
        Set(--index);
        ret=ETrue;
        } // else the first one, no more to go and return false
    return ret;
    }

// -----------------------------------------------------------------------------
// Sets path to first item
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::SetToFirst()
    {
    Set(0);
    }

// -----------------------------------------------------------------------------
// Sets path to last item
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::SetToLast()
    {
    Set(Count()-1);
    }

// -----------------------------------------------------------------------------
// Change current item by index at top level
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Set(TInt aIndex)
    {
    MPX_ASSERT(Levels());
    CMPXCollectionPathNode& topLevel = TopLevel();
    // RArray will panic if aIndex out of bound
    topLevel.Set(iIds[aIndex], aIndex);
    }

// -----------------------------------------------------------------------------
// Change current item by id at top level
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Set(const TMPXItemId& aId)
    {
    TInt index=IndexOfId(aId);
    MPX_ASSERT(KErrNotFound!=index && Levels());
    CMPXCollectionPathNode& topLevel = TopLevel();
    topLevel.Set(aId, index);
    }

// -----------------------------------------------------------------------------
// Set open mode for next level
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Set(TMPXOpenMode aMode)
    {
    MPX_ASSERT(Levels());
    TopLevel().Set(aMode);
    }

// -----------------------------------------------------------------------------
// Set open attrobutes for next level
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Set(const TArray<TMPXAttribute>& aAttrs)
    { // DEPRECATED
    TRAP_IGNORE(SetL(aAttrs));
    }

// -----------------------------------------------------------------------------
// Set open attrobutes for next level
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::SetL(const TArray<TMPXAttribute>& aAttrs)
    {
    MPX_ASSERT(Levels());
    TopLevel().SetL(aAttrs);
    }

// -----------------------------------------------------------------------------
//  Select an item by id in the path
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::SelectL(const TMPXItemId& aId)
    {
    TInt index=IndexOfId(aId);
    MPX_ASSERT(KErrNotFound != index);
    SelectL(index);
    }

// -----------------------------------------------------------------------------
//  Select an item by index in the path
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::SelectL(TInt aIndex)
    {
    MPX_ASSERT_EX(aIndex>=0 && aIndex<iIds.Count(), EBadArrayIndex);
    TInt err = iSelection.InsertInOrder(aIndex);
    if (KErrNone!=err && KErrAlreadyExists!=err)
        { // ignore duplicated items
        User::Leave(err);
        }
    }

// -----------------------------------------------------------------------------
//  Select an item by index in the path
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::SelectAllL()
    {
    iSelection.Reset();
    for (TInt i=0; i < iIds.Count(); ++i)
        {
        iSelection.AppendL(i);
        }
    }

// -----------------------------------------------------------------------------
//  Deselects an item by id in the path
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Deselect(const TMPXItemId& aId)
    {
    TInt index=IndexOfId(aId);
    MPX_ASSERT(KErrNotFound != index);
    Deselect(index);
    }

// -----------------------------------------------------------------------------
//  Deselects an item by index in the path
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Deselect(TInt aIndex)
    {
    MPX_ASSERT_EX(aIndex>=0 && aIndex<iIds.Count(), EBadArrayIndex);
    TInt index(iSelection.FindInOrder(aIndex));
    MPX_ASSERT(KErrNotFound !=index);
    iSelection.Remove(index);
    }

// -----------------------------------------------------------------------------
//  Select an item by index in the path
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::DeselectAll()
    {
    iSelection.Reset();
    }

// -----------------------------------------------------------------------------
//  Removes an item in the path
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Remove(const TMPXItemId& aId)
    {
    TInt index=IndexOfId(aId);
    MPX_ASSERT(KErrNotFound != index);
    Remove(index);
    }

// -----------------------------------------------------------------------------
//  Removes an item in the path
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Remove(TInt aIndex)
    {
    iIds.Remove(aIndex);
    TInt ret = iSelection.FindInOrder(aIndex);
    if (KErrNotFound != ret)
        {
        iSelection.Remove(ret);
        TInt count = iSelection.Count();
        for (TInt i=ret; i<count; i++)
            {
            --iSelection[i];
            }
        }
    }

// -----------------------------------------------------------------------------
//  Query selection
// -----------------------------------------------------------------------------
//
EXPORT_C TBool CMPXCollectionPath::IsSelected(const TMPXItemId& aId) const
    {
    TBool ret(EFalse);
    TInt index=IndexOfId(aId);
    if (KErrNotFound != index)
        {
        ret = IsSelected(index);
        }
    return ret;
    }

// -----------------------------------------------------------------------------
//  Query selection
// -----------------------------------------------------------------------------
//
EXPORT_C TBool CMPXCollectionPath::IsSelected(TInt aIndex) const
    {
    return iSelection.FindInOrder(aIndex) != KErrNotFound;
    }

// -----------------------------------------------------------------------------
//  Clears selection
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::ClearSelection()
    {
    iSelection.Reset();
    }

// -----------------------------------------------------------------------------
// Array of currently selected indices
// -----------------------------------------------------------------------------
//
EXPORT_C TArray<TInt> CMPXCollectionPath::Selection() const
    {
    return iSelection.Array();
    }

// -----------------------------------------------------------------------------
// Current selected IDs
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::SelectionL(RArray<TMPXItemId>& aIds) const
    {
    aIds.Reset();
    TInt idCount(iIds.Count());
    TInt selCount(iSelection.Count());
    for  (TInt i=0; i<selCount; ++i)
        {
        TInt idIndex(iSelection[i]);
        if (idIndex < idCount)
            {
            aIds.AppendL(iIds[idIndex]);
            }
        }
    }

// -----------------------------------------------------------------------------
// Update the item id at a particular index
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Update( TInt aIndex, TMPXItemId& aNewId )
    {
    MPX_ASSERT( aIndex >= 0 && aIndex < iIds.Count() );
    iIds[aIndex] = aNewId;
    }

// -----------------------------------------------------------------------------
// Index of current item at top level
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CMPXCollectionPath::Index() const
    {
    TInt ret(KErrNotFound);
    if (Levels())
        {
        ret = TopLevel().Index();
        }
    return ret;
    }

// -----------------------------------------------------------------------------
// Return current item id at top level
// -----------------------------------------------------------------------------
//
EXPORT_C const TMPXItemId& CMPXCollectionPath::Id() const
    {
    if (Levels())
        {
        return TopLevel().Id();
        }
    else
        {
        return iInvalidId;
        }
    }

// -----------------------------------------------------------------------------
// Returns the number of items at top level
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CMPXCollectionPath::Count() const
    {
    return iIds.Count();
    }

// -----------------------------------------------------------------------------
// Returns the open mode for the next level
// -----------------------------------------------------------------------------
//
EXPORT_C TMPXOpenMode CMPXCollectionPath::OpenNextMode() const
    {
    return Levels() ? TopLevel().OpenMode():EMPXOpenGroupOrPlaylist;
    }

// -----------------------------------------------------------------------------
// Returns the open mode for the previous level
// -----------------------------------------------------------------------------
//
EXPORT_C TMPXOpenMode CMPXCollectionPath::OpenPreviousMode() const
    {
    TInt n=Levels();
    return n>1?iNodeArray[n-2]->OpenMode():EMPXOpenGroupOrPlaylist;
    }

// -----------------------------------------------------------------------------
// Index from id
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CMPXCollectionPath::IndexOfId(const TMPXItemId& aId) const
    {
    TInt ret(KErrNotFound);
    for (TInt i=iIds.Count(); --i>=0; )
        {
        if (aId == iIds[i])
            {
            ret = i;
            break;
            }
        }
    return ret;
    }

// -----------------------------------------------------------------------------
// Return item id at top specific level for a specific item
// -----------------------------------------------------------------------------
//
EXPORT_C const TMPXItemId& CMPXCollectionPath::IdOfIndex( TInt aIndex ) const
    {
    if( aIndex >=0 && aIndex < iIds.Count() )
        {
        return iIds[aIndex];
        }
    else
        {
        return iInvalidId;
        }
    }

// -----------------------------------------------------------------------------
// Return item index at a specific level
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CMPXCollectionPath::Index(TInt aLevel) const
    {
    MPX_ASSERT(aLevel>=0 && aLevel < Levels());
    return iNodeArray[aLevel]->Index();
    }

// -----------------------------------------------------------------------------
// Return item id at a specific level
// -----------------------------------------------------------------------------
//
EXPORT_C const TMPXItemId& CMPXCollectionPath::Id(TInt aLevel) const
    {
    if( aLevel < iNodeArray.Count() && aLevel >= 0 )
        {
        return iNodeArray[aLevel]->Id();
        }
    else
        {
        return iInvalidId;
        }
    }

// -----------------------------------------------------------------------------
// Returns the depth into the collection
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CMPXCollectionPath::Levels() const
    {
    return iNodeArray.Count();
    }

// -----------------------------------------------------------------------------
// Remove a level from path
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Back()
    {
    TInt levels = Levels();
    MPX_ASSERT(levels > 0);
    CMPXCollectionPathNode* node = iNodeArray[levels-1];
    iNodeArray.Remove(levels-1);
    delete node;
    iIds.Reset();
    iSelection.Reset();
    }

// -----------------------------------------------------------------------------
// Append a new level
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::AppendL(const TArray<TMPXItemId>& aIds)
    {
    iIds.Reset();
    iSelection.Reset();
    CMPXCollectionPathNode* node(NULL);
    if (aIds.Count())
        {
        ::CopyArrayL(aIds, iIds);
        node = CMPXCollectionPathNode::NewLC();
        node->Set(iIds[0], 0);
        iNodeArray.AppendL(node);
        CleanupStack::Pop(node);
        }
    else
        {// add a level in order to support back
        node = CMPXCollectionPathNode::NewLC();
        node->Set(iInvalidId, KErrNotFound);
        iNodeArray.AppendL(node);
        CleanupStack::Pop(node);
        }
    }

// -----------------------------------------------------------------------------
// Append a new level
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::AppendL(const TMPXItemId& aId)
    {
    RArray<TMPXItemId> ids;
    CleanupClosePushL(ids);
    ids.AppendL(aId);
    AppendL(ids.Array());
    CleanupStack::PopAndDestroy(&ids);
    }

// -----------------------------------------------------------------------------
// Append an id
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::InsertL(const TMPXItemId& aId, TInt aPos)
    {
    MPX_ASSERT(iIds.Count());
    iIds.InsertL(aId, aPos);
    // update the selection
    for (TInt i=0; i<iSelection.Count(); ++i)
        {
        TInt& sel = iSelection[i];
        if (sel >=aPos)
            {
            ++sel;
            }
        }
    }

// -----------------------------------------------------------------------------
// Resets the collection path object
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::Reset()
    {
    iNodeArray.ResetAndDestroy();
    iIds.Reset();
    iSelection.Reset();
    }

// -----------------------------------------------------------------------------
// Creates a collection path pointing to the container
// -----------------------------------------------------------------------------
//
EXPORT_C CMPXCollectionPath* CMPXCollectionPath::ContainerPathL() const
    {
    CMPXCollectionPath* p = CMPXCollectionPath::NewL();
    CleanupStack::PushL( p );

    TInt count = iNodeArray.Count() - 1;  // Copy up to container level
    for (TInt i=0; i<count; ++i)
        {
        CMPXCollectionPathNode* node =
            CMPXCollectionPathNode::NewLC(*iNodeArray[i]);
        p->iNodeArray.AppendL(node);
        CleanupStack::Pop(node);
        }

    CleanupStack::Pop( p );
    return p;
    }

// -----------------------------------------------------------------------------
// Update collection path. When a item changed in the collection plugin,
// collection plugin calls back collection context, which in turn updates
// its collection paths by calling this function
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CMPXCollectionPath::HandleChange(
    const TUid& aCollectionId,
    const TMPXItemId& aId,
    const TMPXItemId& aDeprecatedId,
    CMPXCollectionPath::TMPXCollectionPathChange aChange,
    TInt& aSelection )
    {
    TBool updated(EPathUnchanged);
    aSelection = KErrNotFound;

    if (aChange == CMPXCollectionPath::EAdded)
        {
        updated = EPathModified;
        }
    else if (aChange == CMPXCollectionPath::EGroupModified )
        {
        TInt levels( Levels() );
        if( levels > 0 )
            {
            // Check if the collection id is correct
            if (iNodeArray[ECollectionUid]->Id() ==
                TMPXItemId(aCollectionId.iUid) && levels > 1)
                {
                // Check if the container level is the one modified
                if( aId == Id( levels-2 ) )
                    {
                    updated = EPathModified;
                    }
                }
            }
        }
    else if (aChange == CMPXCollectionPath::EDeleted ||
             aChange == CMPXCollectionPath::EModified)
        {
        TInt levels = Levels();
        if (levels > 0)
            {
            // check collection id level, aka root level
            if (iNodeArray[ECollectionUid]->Id() ==
                TMPXItemId(aCollectionId.iUid) && levels > 1)
                {
                // check node array except top level and root level.
                for (TInt i=ECollectionRoot+1; i<levels-1; ++i)
                    {
                    if (iNodeArray[i]->Id() == aId ||
                        (aDeprecatedId != 0 && iNodeArray[i]->Id() == aDeprecatedId))
                        {
                        if( aChange == CMPXCollectionPath::EModified &&
                            aDeprecatedId != 0 )
                            {
                            // If the item is modified and the item id is updated
                            // we simply replace the node level item id with the new
                            // item id
                            //
                            // Continue and check other levels if any other
                            // levels are using this ID. ie: all songs of an artist
                            // The item IDs need to be unique across all levels, but
                            // can have duplicates. This is in the case of a playlist with
                            // more than 1 instance of a song
                            //
                            CMPXCollectionPathNode* node = iNodeArray[i];
                            node->Set( aId, Index(i) );
                            updated = EPathModified;
                            }
                        else
                            {
                            // Trim the path and break out of the loop
                            aSelection = Index(i);
                            for (TInt j=Levels()-1; j>i; --j)
                                {
                                CMPXCollectionPathNode* node = iNodeArray[j];
                                iNodeArray.Remove(j);
                                delete node;
                                }
                            updated = EPathClipped;
                            break;
                            }
                        }
                    }
                 // Check the top level
                 if (!updated)
                    {
                    TMPXItemId search = aDeprecatedId != 0? aDeprecatedId : aId;
                    TInt temp(KErrNotFound);

                    // Unmatched ids in the item ID,
                    // try to look for an exact match first
                    //
                    if( search.iId1 != search.iId2 )
                        {
                        for (TInt i=iIds.Count(); --i>=0; )
                            {
                            if (search == iIds[i] )
                                {
                                temp = i;
                                break;
                                }
                            }
                        }
                    // Still not found, check for approximate equal
                    //
                    if( KErrNotFound == temp )
                        {
                        for (TInt i=iIds.Count(); --i>=0; )
                            {
                            if (search.ApproxEqual(iIds[i]))
                                {
                                temp = i;
                                break;
                                }
                            }
                        }
                    if (KErrNotFound != temp)
                        { // Improvement: only remove item deleted
                        updated = EPathModified;
                        aSelection = temp;
                        }
                    }

                // Only clean up level if the path has been clipped
                if (updated == EPathClipped )
                    {
                    // Reset top level
                    iIds.Reset();
                    iSelection.Reset();
                    }
                }
            }
        }
    return updated;
    }

// -----------------------------------------------------------------------------
// CMPXCollectionPath::OpenAttributes
// -----------------------------------------------------------------------------
//
EXPORT_C const TArray<TMPXAttribute> CMPXCollectionPath::OpenAttributes() const
    {
    MPX_ASSERT(Levels()>0);
    return TopLevel().OpenAttributes();
    }

// -----------------------------------------------------------------------------
// Get top level items
// -----------------------------------------------------------------------------
//
EXPORT_C const TArray<TMPXItemId> CMPXCollectionPath::Items() const
    {
    MPX_ASSERT(Levels()>0);
    return iIds.Array();
    }

// -----------------------------------------------------------------------------
// Internalize object
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::InternalizeL(RReadStream& aStream)
    {
    (void)aStream.ReadInt32L(); // Discard object type
    iNodeArray.ResetAndDestroy();
    ::InternalizeCObjectArrayL(iNodeArray, aStream);
    iIds.Reset();
    MPXUser::InternalizeL(iIds, aStream);
    iSelection.Reset();
    ::InternalizeL(iSelection, aStream);
    }

// -----------------------------------------------------------------------------
// Externalize object
// -----------------------------------------------------------------------------
//
EXPORT_C void CMPXCollectionPath::ExternalizeL(RWriteStream& aStream) const
    {
    aStream.WriteInt32L(MMPXData::EPath);
    ::ExternalizeL(iNodeArray.Array(), aStream);
    MPXUser::ExternalizeL(iIds.Array(), aStream);
    ::ExternalizeL(iSelection.Array(), aStream);
    }

// -----------------------------------------------------------------------------
// Top level
// -----------------------------------------------------------------------------
//
CMPXCollectionPathNode& CMPXCollectionPath::TopLevel()
    {
    MPX_ASSERT(Levels() > 0);
    return *iNodeArray[Levels()-1];
    }

// -----------------------------------------------------------------------------
// Top level
// -----------------------------------------------------------------------------
//
const CMPXCollectionPathNode& CMPXCollectionPath::TopLevel() const
    {
    MPX_ASSERT(Levels() > 0);
    return *iNodeArray[Levels()-1];
    }