upnpmediaserver/contentdirectoryservice/src/upnpcontentdirectorydb.cpp
author Sampo Huttunen <sampo.huttunen@nokia.com>
Wed, 24 Nov 2010 09:39:46 +0200
branchIOP_Improvements
changeset 45 a6c41ca11adf
parent 38 5360b7ddc251
permissions -rw-r--r--
Updated the SIS package, there was some BC issue with the earlier version. Also updated the platform UID to S^3 version.

/** @file
* Copyright (c) 2005-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:  Database for MediaServer
 *
*/


// INCLUDE FILES
#include <barsread.h>
#include <barsc.h>   
#include <f32file.h>
#include <xmlengdom.h>
#include <sysutil.h>
#include <upnpdominterface.h>
#include <upnpstring.h>
#include "upnpprotocolinfo.h"

#include "upnpfileutils.h"
#include "upnpcontentdirectorydb.h"
#include "upnpcontentdirectoryglobals.h"
#include "upnpelementfactory.h"
#include "upnpelementbean.h"
#include "upnpobjectbean.h"
#include "upnpattributebean.h"
#include "upnpfilterelement.h"
#include "upnpresourcesbean.h"
#include "upnpcdutils.h"
#include "upnpcddbfactory.h"
#include "upnpmetadatastorage.h"

#define KLogFile _L("ContentDirectoryDb.log")
#include "upnpcustomlog.h"

// ============================= LOCAL FUNCTIONS ===============================
// -----------------------------------------------------------------------------
// CleanupFilterElementPointerArray
// Used by TCleanupItem to clean a RPointerArray<CUpnpFilterElement>
// -----------------------------------------------------------------------------
//
void CleanupFilterElementPointerArray( TAny* aArray )
    {
    (reinterpret_cast<RPointerArray<CUpnpFilterElement>*> (aArray))->ResetAndDestroy( );
    }

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

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::CUpnpContentDirectoryDb
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CUpnpContentDirectoryDb::CUpnpContentDirectoryDb(
    CUpnpMetadataStorage* aMetadataStorage )
    {
    iMetadataStorage =aMetadataStorage;
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::ConstructL()
    {
    iDOMImpl.OpenL( );
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::NewLC
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CUpnpContentDirectoryDb* CUpnpContentDirectoryDb::NewLC(
    CUpnpMetadataStorage* aMetadataStorage )
    {
    CUpnpContentDirectoryDb* self = new ( ELeave ) CUpnpContentDirectoryDb(aMetadataStorage);
    CleanupStack::PushL( self );
    self->ConstructL( );
    return self;
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CUpnpContentDirectoryDb* CUpnpContentDirectoryDb::NewL(
    CUpnpMetadataStorage* aMetadataStorage )
    {
    CUpnpContentDirectoryDb* self = NewLC( aMetadataStorage );
    CleanupStack::Pop( self );
    return self;
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::~~CUpnpContentDirectoryDb
// Destructor
// -----------------------------------------------------------------------------
//
CUpnpContentDirectoryDb::~CUpnpContentDirectoryDb()
    {
    iDOMImpl.Close( );
    LOGCD( "CUpnpContentDirectoryDb::~CUpnpContentDirectoryDb", "", 0,
        "DataBase closed" );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::InsertObjectL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::InsertObjectL(
    RXmlEngDocument& aFragment, TInt aContainerId, TInt* aId )
    {
    TUpnpErrorCode ret( EUpnpUndefined );
    // get object element
    TXmlEngElement element = UpnpCdUtils::GetObjectElementL( aFragment );

    // start transaction
    iMetadataStorage->BeginTransactionL( );

    TRAPD( err, ret = DoInsertObjectL( element, aContainerId, aId ) );
    if ( err )
        { // rollback if error
        iMetadataStorage->RollbackTransactionL( );
        User::Leave( err );
        }
    iMetadataStorage->CommitTransactionL( );

    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjectListL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::GetObjectListL( TInt aParentId,
    RArray<TInt>& aList )
    {
    return iMetadataStorage->GetObjectListL( aParentId, aList );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjectListL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::GetObjectListL( const RArray<TInt>& aIds,
    const TDesC8& aFilter, RArray<RXmlEngDocument>& aObjs )
    {
    for ( TInt i = 0; i < aIds.Count( ); i++ )
        {
        RXmlEngDocument obj;
        CleanupClosePushL( obj );
        GetObjectL( aIds[i], obj, aFilter );
        aObjs.AppendL( obj );
        CleanupStack::Pop( &obj );
        }
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::DeleteObjectL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::DeleteObjectL( TInt aObjId,
    RArray<TInt>& aRefList, TBool aDeleteResource )
    {
    return iMetadataStorage->DeleteObjectL( aObjId, aRefList, aDeleteResource );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::UpdateObjectL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::UpdateObjectL( TInt aObjId,
    RXmlEngDocument& aFragment )
    {
    TUpnpErrorCode ret = EUpnpUndefined;

    // // get object element
    TXmlEngElement element = UpnpCdUtils::GetObjectElementL( aFragment );

    // start transaction
    iMetadataStorage->BeginTransactionL( );

    TRAPD( err, ret = DoUpdateObjectL( aObjId, element ) );
    if ( err )
        { // rollback - error
        iMetadataStorage->RollbackTransactionL( );
        User::Leave( err );
        }
    // commit if success
    iMetadataStorage->CommitTransactionL( );

    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::DoUpdateObjectL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::DoUpdateObjectL( TInt aObjId,
    TXmlEngElement aFragment )
    {
    TUpnpErrorCode ret = EUpnpUndefined;

    // delete object
    ret = iMetadataStorage->DoDeleteObjectL( aObjId, EFalse, EFalse );
    TXmlEngElement el = aFragment;
    ret = InsertUpdatedL( aObjId, el );
    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::InsertUpdatedL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::InsertUpdatedL( TInt aObjId,
    TXmlEngElement& aElement )
    {
    TUpnpErrorCode ret = EUpnpUndefined;
    iMetadataStorage->InsertObjectIntoObjectTableL( aElement );
    ret = EUpnpOk;
    RXmlEngNodeList<TXmlEngElement> elements;
    CleanupClosePushL( elements );
    aElement.GetChildElements( elements );

    while ( elements.HasNext( ) )
        {
        TXmlEngElement el = elements.Next( );
        if ( el.Name().CompareF( KClassTagName ) && el.Name().CompareF( KTitleTagName ) )
            {
            ret = InsertElementL( el, aObjId );
            }
        else
            {
            UpnpCdUtils::IsElementRequiredL( el ); // just remove the marker attr
            }
        }
    CleanupStack::PopAndDestroy( &elements );
    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::InsertElementL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::InsertElementL(
    const TXmlEngElement& aElement, TInt aObjId )
    {
    return InsertAttributesL( aElement, iMetadataStorage->InsertElementL(
        aElement, aObjId ), aObjId );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::PrepareDidlDocumentL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
RXmlEngDocument CUpnpContentDirectoryDb::PrepareDidlDocumentL()
    {
    RXmlEngDocument doc;
    doc.OpenL( iDOMImpl );
    TXmlEngElement el = doc.CreateDocumentElementL( KDidlLite( ) );
    el.AddNamespaceDeclarationL( KXmlnsNsUri( ), KCmlnsNsPref( ) );
    el.AddNamespaceDeclarationL( KDcNsUri( ), KDcNsPref( ) );
    el.AddNamespaceDeclarationL( KUpnpNsUri( ), KUpnpNsPref( ) );
    return doc;

    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::CheckObjectRestrictionL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::CheckObjectRestrictionL( TInt aObjId )
    {
    return iMetadataStorage->CheckObjectRestrictionL( aObjId );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjectL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
TUpnpErrorCode CUpnpContentDirectoryDb::GetObjectL( TInt aObjectID,
    RXmlEngDocument& aDoc, const TDesC8& aFilterString )
    {
    TUpnpErrorCode ret = EUpnpUndefined;

    if ( aFilterString == KAsterisk8 )
        { // get the whole object
        ret = DoGetObjectL( aObjectID, aDoc, ETrue );
        }
    else if ( !aFilterString.Length( ) )
        { // get only required properties
        ret = DoGetObjectL( aObjectID, aDoc, EFalse );
        }
    else
        {
        RXmlEngDocument doc; // before the whole obj is created do not change the given reference,
        CleanupClosePushL( doc ); // it prevents from returning not completed object if something goes wrong 
        // during construction of response object
        // get required properties
        ret = DoGetObjectL( aObjectID, doc, EFalse );
        if ( ret == EUpnpOk ) // do not not continue if something went wrong
            {
            // prepare a list of filter elements
            RPointerArray<CUpnpFilterElement> filterElements;
            TCleanupItem cleanupItem( CleanupFilterElementPointerArray,
                &filterElements );
            CleanupStack::PushL( cleanupItem );
            // parse filter string
            ParseFilterStringL( aFilterString, filterElements );
            // add requested elements
            AddRequestedPropertiesL( aObjectID,
                UpnpCdUtils::GetObjectElementL( doc ), filterElements ); // ignore if error
            //clean up
            CleanupStack::PopAndDestroy( &filterElements );
            }
        if ( ret == EUpnpOk )
            { // object is completed
            aDoc = doc;
            CleanupStack::Pop( &doc );
            }
        else
            { // something went wrong
            CleanupStack::PopAndDestroy( &doc );
            }

        }

    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjectL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
TUpnpErrorCode CUpnpContentDirectoryDb::AddRequestedPropertiesL(
    TInt aObjectID, TXmlEngElement aElement,
    const RPointerArray<CUpnpFilterElement>& aFilterElements )
    {
    TUpnpErrorCode ret = EUpnpUndefined;
    // aFilterElements must be sorted
    for ( TInt i = 0; i < aFilterElements.Count( ); i++ )
        {
        const CUpnpFilterElement* filter = aFilterElements[i];
        if ( filter->ElementName().Length( ) )
            { // element part defined
            AddElementL( aElement, aObjectID, filter );
            }
        else
            { // element part not defined - it is attribute of main element tag
            for ( TInt j = 0; j < filter->Count( ); j++ )
                {
                if ( (*filter)[j] == KChildCount )
                    {
                    SetChildCountL( aElement, aObjectID );
                    ret = EUpnpOk;
                    }
                else
                    {
                    ret = iMetadataStorage->AddMainTagAttrL( aElement,
                        aObjectID, (*filter)[j] );
                    }
                }
            }
        }
    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::SetChildCountL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
void CUpnpContentDirectoryDb::SetChildCountL( TXmlEngElement aElement,
    TInt aId )
    {
    if ( aElement.Name( ) == KContainer )
        {
        // value
        TInt childCount = iMetadataStorage->GetChildCountL( aId );
        TBuf8<KMaxIntegerLen> num;
        num.Num( childCount );

        // is it already exist?
        TXmlEngAttr childAttr = aElement.AttributeNodeL( KChildCount( ) );
        if ( childAttr.IsNull( ) )
            { // not exist - add 
            aElement.AddNewAttributeL( KChildCount( ), num );
            }
        else
            { // already exists - update
            childAttr.SetValueL( num );
            }
        }
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::AddElementL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
void CUpnpContentDirectoryDb::AddElementL( TXmlEngElement aElement,
    TInt aObjectID, const CUpnpFilterElement* aReqElement )
    {
    // get elements from database
    RDbView view = iMetadataStorage->GetElementViewL( aObjectID,
        aReqElement->ElementName( ) );
    CleanupClosePushL( view );

    // for each element found
    while ( view.NextL( ) )
        {
        view.GetL( );
        CUpnpElementBean* elBean = CUpnpElementBean::NewLC( view );

        // add element
        TXmlEngElement newElement = elBean->AttachElmL( aElement );

        // add required attributes
        AddAttributesL( elBean->ElmId( ), aObjectID, newElement, EFalse );

        // for each requested attribute
        for ( TInt i = 0; i < aReqElement->Count( ); i++ )
            {
            TPtrC8 attrNamePtr((*aReqElement)[i]);

            TXmlEngAttr attr = newElement.AttributeNodeL( attrNamePtr );
            // add if not present
            // ignore if element has not any attribute
            if ( attr.IsNull( ) && elBean->ElmHasAttribute( ) )
                {
                AddAttributeL( newElement, attrNamePtr, elBean->ElmId( ),
                    aObjectID );
                }
            }
        // clean up
        CleanupStack::PopAndDestroy( elBean );
        }
    // clean up
    CleanupStack::PopAndDestroy( &view );
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::ParseFilterStringL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::ParseFilterStringL( const TDesC8& aFilter,
    RPointerArray<CUpnpFilterElement>& aList )
    {
    // separate filter's parts
    CDesC8ArrayFlat* list = new(ELeave) CDesC8ArrayFlat(KDefaultGranularity);
    CleanupStack::PushL( list );
    TInt commaPos;
    TPtrC8 filter(aFilter);
    while ( (commaPos = filter.Find( KCommaStr8( ) ) ) != KErrNotFound )
        {
        HBufC8* buf = filter.Left(commaPos).AllocLC( );
        buf->Des().TrimAll( );
        list->AppendL( *buf );
        filter.Set( filter.Mid( commaPos+1 ) );
        CleanupStack::PopAndDestroy( buf );
        }
    // add last
    HBufC8* buf = filter.AllocLC( );
    buf->Des().TrimAll( );
    list->AppendL( *buf );
    filter.Set( filter.Mid( commaPos+1 ) );
    CleanupStack::PopAndDestroy( buf );

    // sort to simplify
    list->Sort( );

    TPtrC8 curr( KMinusOne8 );
    for ( TInt i = 0; i< list->Count( ); i++ ) // for each filter property
        {
        // split filter string - element@attribute
        TInt atPos = (*list)[i].Find( KAtStr8 );
        TPtrC8 attr;
        if ( atPos == KErrNotFound ) // is it element name
            { // it is element
            attr.Set( KNullDesC8 );
            atPos = (*list)[i].Length( );
            }
        else
            {
            attr.Set( (*list)[i].Mid( atPos+1 ) );
            }
        TPtrC8 element((*list)[i].Left( atPos ) );

        // do not add "class" and "title" elements - required by default
        if ( element.CompareF( KClassTagNameWithNs8 )
                && element.CompareF( KTitleTagNameWithNs8 ) )
            { // it is neither a class nor title element
            if ( element == curr )
                { // already exists, add next attribute
                if ( attr.Length( ) ) // ignore empty attr
                    aList[aList.Count()-1]->AppendAttributeL( attr );
                }
            else
                { // create new filter element
                CUpnpFilterElement* tmp = CUpnpFilterElement::NewLC( );
                tmp->SetElementNameL( element );
                if ( attr.Length( ) ) // ignore epmty attr
                    tmp->AppendAttributeL( attr );
                aList.AppendL( tmp );
                CleanupStack::Pop( tmp );
                curr.Set( element );
                }
            }
        }
    // clean up
    CleanupStack::PopAndDestroy( list );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::DoGetObjectL
// Function returns DIDL-Lite XML fragment with metadata for requested object.
// IMPORTANT: 1. Caller takes responsibility for returned document and is 
//              obliged to close it.
//           2. The root element of the document is <DIDL-Lite> element and
//              the object's element is its direct, onluy child.
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::DoGetObjectL( TInt aObjectID,
    RXmlEngDocument& aDoc, TBool aAll )
    {
    TUpnpErrorCode ret = EUpnpUndefined;
    RDbView view = iMetadataStorage->GetObjViewL( aObjectID );
    CleanupClosePushL( view );

    // Get result
    if ( view.FirstL( ) )
        {
        // get row
        view.GetL( );

        // prepare DIDL-Lite
        RXmlEngDocument doc;
        CleanupClosePushL( doc );
        doc = PrepareDidlDocumentL( );

        // object bean      
        CUpnpObjectBean* objBean = CUpnpObjectBean::NewLC( view );
        TXmlEngElement objElement;

        if ( aAll )
            { // all object's properties
            objElement
                    = objBean->AttachWholeObjectElL( doc.DocumentElement( ) );
            // childCount
            SetChildCountL( UpnpCdUtils::GetObjectElementL( doc ),
                objBean->ObjId( ) );
            }
        else
            { // only required
            objElement = objBean->AttachObjectElL( doc.DocumentElement( ) );
            }

        // clean up
        CleanupStack::PopAndDestroy( objBean );

        // aObjectID might be a reference object, so trying to get real object id        
        TInt realObjectId = ReferedObjectIdL( aObjectID );

        // add elements to the tree
        AddElementsL( realObjectId, objElement, aAll );

        // assign to the given reference
        aDoc = doc;
        // everything went right
        ret = EUpnpOk;
        // do not remove doc
        CleanupStack::Pop( &doc );
        }
    else
        { // the is no such object
        ret = ENoSuchObject;
        }
    // clean up
    CleanupStack::PopAndDestroy( &view );

    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::AddElementsL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::AddElementsL( TInt aObjectID,
    TXmlEngElement aObjectElement, TBool aAll )
    {
    RDbView view = iMetadataStorage->GetElementsViewL( aObjectID, aAll );
    CleanupClosePushL( view );
    // Iterate through elements
    while ( view.NextL( ) )
        {
        view.GetL( );
        CUpnpElementBean* elBean = CUpnpElementBean::NewLC( view );

        // add new element
        TXmlEngElement newElement = elBean->AttachElmL( aObjectElement );

        // add attributes
        AddAttributesL( elBean->ElmId( ), aObjectID, newElement, aAll );

        // clean up
        CleanupStack::PopAndDestroy( elBean );
        }
    // clean up
    CleanupStack::PopAndDestroy( &view );
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::AddAttributesL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//  
void CUpnpContentDirectoryDb::AddAttributesL( TInt aElementId,
    TInt aObjectID, TXmlEngElement& aElement, TBool aAll )
    {
    RDbView view = iMetadataStorage->GetAttributesViewByObjectIdL( aObjectID,
        aAll );
    CleanupClosePushL( view );

    CDbColSet* colSet = view.ColSetL( );
    const TInt elmIdColNo = colSet->ColNo( KAtrElmIdColName );
    delete colSet;

    // Iterate through attributes
    while ( view.NextL( ) )
        {
        view.GetL( );

        if ( view.ColInt( elmIdColNo ) == aElementId )
            {
            CUpnpAttributeBean* atr = CUpnpAttributeBean::NewLC( view );
            atr->AttachAttrL( aElement );
            CleanupStack::PopAndDestroy( atr );
            }
        }
    // clean up
    CleanupStack::PopAndDestroy( &view );
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::DoInsertObjectL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::DoInsertObjectL(
    TXmlEngElement& aElement, TInt aContainerId, TInt* aId )
    {
    TUpnpErrorCode ret = EUpnpUndefined;

    *aId = SetObjectIdL( aElement );
    UpnpCdUtils::SetContainerIdL( aElement, aContainerId );
    iMetadataStorage->InsertObjectIntoObjectTableL( aElement );
    ret = EUpnpOk;
    RXmlEngNodeList<TXmlEngElement> elements;
    CleanupClosePushL( elements );
    aElement.GetChildElements( elements );

    while ( elements.HasNext( ) )
        {
        TXmlEngElement el = elements.Next( );
        if ( el.Name().CompareF( KClassTagName ) && el.Name().CompareF( KTitleTagName ) )
            {
            ret = InsertElementL( el, *aId );
            }
        else
            {
            UpnpCdUtils::IsElementRequiredL( el ); // just remove the marker attr
            }
        }
    // set object childCount
    SetChildCountL( aElement, *aId );

    CleanupStack::PopAndDestroy( &elements );
    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::InsertAttributesL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::InsertAttributesL(
    const TXmlEngElement aElement, TInt aElmId, TInt aObjId )
    {
    TUpnpErrorCode ret = EUpnpUndefined;
    RXmlEngNodeList<TXmlEngAttr> list;
    CleanupClosePushL( list );
    aElement.GetAttributes( list );
    if ( list.Count( ) )
        {
        RArray<TXmlEngAttr> attributes; // to store real attr
        CleanupClosePushL( attributes );
        RArray<TXmlEngAttr> markers; // to store marker attr
        CleanupClosePushL( markers );
        // separate markers from attr
        while ( list.HasNext( ) )
            {
            TXmlEngAttr attr = list.Next( );
            if ( attr.Name().Match( KRequiredAtrSufPattern ) == KErrNotFound )
                {
                attributes.AppendL( attr );
                }
            else
                {
                markers.AppendL( attr );
                }
            }

        // insert attributes
        for ( TInt i = 0; i < attributes.Count( ); i++ )
            {
            iMetadataStorage->InsertAttributeL( attributes[i], aElmId,
                IsAttrRequiredL( attributes[i], markers ), aObjId );
            ret = EUpnpOk;
            }
        // remove markers from DOM tree
        for ( TInt i = 0; i < markers.Count( ); i++ )
            {
            markers[i].Remove( );
            }
        // clean up
        CleanupStack::PopAndDestroy( &markers );
        CleanupStack::PopAndDestroy( &attributes );
        }
    else
        {
        ret = EUpnpOk;
        }

    CleanupStack::PopAndDestroy( &list );
    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::IsAttrRequiredL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TBool CUpnpContentDirectoryDb::IsAttrRequiredL( const TXmlEngAttr& aAttr,
    RArray<TXmlEngAttr>& aMarkers )
    {
    TBool ret = EFalse;

    for ( TInt i = 0; i< aMarkers.Count( ); i++ )
        {
        if ( aMarkers[i].Name().Find( aAttr.Name( ) ) == 0 )
            { // attr required
            ret = ETrue;
            break;
            }
        }

    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::SetObjectIdL
// Sets the id val in the objected to be created
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::SetObjectIdL( TXmlEngElement& aElement )
    {
    TInt ret = iMetadataStorage->GetNextKeyL( KObjectTableName8 );
    UpnpCdUtils::SetObjectIdL( aElement, ret );
    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjectIdL
// Gets the id val in the objected to be created
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::GetObjectIdL()
    {
    TInt ret = iMetadataStorage->NextKeyL( KObjectTableName8 );

    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjectIdL
// Gets the id val in the objected to be created
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
HBufC8* CUpnpContentDirectoryDb::GetObjectTitleL( TInt aId )
    {
    return iMetadataStorage->GetObjectTitleL( aId );
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::ReferedObjectIdL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::ReferedObjectIdL( TInt aObjectId )
    {
    TInt ret = KErrNotFound;

    TInt refId = iMetadataStorage->ReferedObjectIdL( aObjectId );

    if ( refId != KErrNotFound )
        {
        ret = ReferedObjectIdL( refId );
        }
    else
        {
        ret = aObjectId;
        }

    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetElIdByAttrL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::GetObjIdByAttrL( const TDesC8& aAttrName,
    const TDesC8& aAttrVal )
    {
    TInt objId = KErrNotFound;

    TInt elmId = iMetadataStorage->GetElmIdForAttrL( aAttrName, aAttrVal );
    if ( elmId != KErrNotFound )
        {
        objId = iMetadataStorage->GetObjIdForElmIdL( elmId );
        }

    return objId;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjIdByAttrLikeL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::GetObjIdByAttrLikeL( const TDesC8& aAttrName,
    const TDesC8& aAttrVal )
    {
    TInt objId = KErrNotFound;

    TInt elmId = iMetadataStorage->GetElmIdForAttrLikeL( aAttrName, aAttrVal );
    if ( elmId != KErrNotFound )
        {
        objId = iMetadataStorage->GetObjIdForElmIdL( elmId );
        }

    return objId;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjectByAttrL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::GetObjectByAttrL(
    RXmlEngDocument& aDoc, TInt* aObjId, const TDesC8& aAttrName,
    const TDesC8& aAttrVal )
    {
    TUpnpErrorCode ret = EUndefined;

    *aObjId = GetObjIdByAttrL( aAttrName, aAttrVal );
    if ( *aObjId >= 0 )
        {
        ret = GetObjectL( *aObjId, aDoc, KAsterisk8 );
        }
    else
        { // something went wrong
        ret = ENoSuchObject;
        }
    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::CheckParentRestrictionL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpContentDirectoryDb::CheckParentRestrictionL( TInt aObjId )
    {
    return iMetadataStorage->CheckParentRestrictionL( aObjId );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::DeleteResourceL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::DeleteResourceL( const TDesC8& aResVal,
    RArray<TInt>& aContIds )
    {
    // start transaction
    iMetadataStorage->BeginTransactionL( );

    TRAPD( err, DoDeleteResourceL( aResVal, aContIds ) );
    if ( err != KErrNone )
        { // rollback - error
        iMetadataStorage->RollbackTransactionL( );
        User::Leave( err );
        }
    // commit if success
    iMetadataStorage->CommitTransactionL( );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::DoDeleteResourceL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::DoDeleteResourceL( const TDesC8& aResVal,
    RArray<TInt>& aContIds )
    {
    HBufC* resVal = HBufC::NewLC( aResVal.Length( ) );
    resVal->Des().Copy( aResVal );

    HBufC* resValReplaced16 = UpnpCdUtils::EscapeAposL( *resVal );
    CleanupStack::PushL( resValReplaced16 );
    HBufC8* resValReplaced8 = UpnpCdUtils::DesToDes8LC( *resValReplaced16 );
    RArray<TInt> objs;
    CleanupClosePushL( objs );
    GetObjectsByResourceL( objs, *resValReplaced16 );

    // not any obj?
    if ( !objs.Count( ) )
        {
        User::Leave( ENoSourceResource );
        }

    TUpnpErrorCode err = EUpnpOk;

    // for each object
    for ( TInt i = 0; i < objs.Count( ); i++ )
        {
        CUpnpObjectBean* objBn = GetObjBeanLC( objs[i] );
        if ( !objBn->ObjRestricted( ) )
            {
            if ( CheckObjectRestrictionL( objBn->ObjParentId( ) ) == EUpnpOk )
                {
                iMetadataStorage->DeleteResElL( *resValReplaced16,
                    objBn->ObjId( ) );
                aContIds.AppendL( objBn->ObjParentId( ) );
                }
            else
                {
                err = ERestrictedParentObject;
                }
            }
        else
            {
            err = ERestrictedObject;
            }
        CleanupStack::PopAndDestroy( objBn );
        }

    // was any deleted
    if ( !aContIds.Count( ) )
        {
        User::Leave( err );
        }

    // if each deleted?
    if ( aContIds.Count( ) == objs.Count( ) )
        {
        // try to fetch resource id
        TRAPD( err, UpnpCdUtils::ResIdFromUriL( *resValReplaced8 ) );
        // ignore if error - resource id could not be fetched
        if ( !err )
            { // OK
            // delete resource
            DeleteResourceByResIdL( UpnpCdUtils::ResIdFromUriL( *resValReplaced8 ) );
            }
        }

    CleanupStack::PopAndDestroy( &objs );
    CleanupStack::PopAndDestroy( resValReplaced8 );
    CleanupStack::PopAndDestroy( resValReplaced16 );
    CleanupStack::PopAndDestroy( resVal );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjBeanLC
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
CUpnpObjectBean* CUpnpContentDirectoryDb::GetObjBeanLC( TInt aObjId )
    {
    CUpnpObjectBean* objBn = 0;

    RDbView view = iMetadataStorage->GetObjViewL( aObjId );
    CleanupClosePushL( view );

    if ( view.FirstL( ) )
        {
        // get values
        view.GetL( );
        objBn = CUpnpObjectBean::NewL( view );
        }

    // clean up 
    CleanupStack::PopAndDestroy( &view );

    CleanupStack::PushL( objBn );
    return objBn;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjectIdsL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::GetObjectsByResourceL( RArray<TInt>& aIds,
    const TDesC& aResVal )
    {

    RDbView view = iMetadataStorage->GetViewOfObjectListForResL( aResVal );
    CleanupClosePushL( view );

    CDbColSet* colSet = view.ColSetL( );
    const TInt colNo = colSet->ColNo( KElmObjIdColName );
    delete colSet;

    // for each row
    while ( view.NextL( ) )
        {
        view.GetL( );
        TInt no = view.ColInt( colNo );
        aIds.AppendL( no );
        }

    // clean up
    CleanupStack::PopAndDestroy( &view );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetProtocolInfoL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::GetProtocolInfoL( const TDesC8& aContentUri,
    CUpnpDlnaProtocolInfo*& aProtocolInfo )
    {
    HBufC* uri = HBufC::NewLC( aContentUri.Length( ) );
    TPtr uriPtr(uri->Des( ));
    uriPtr.Copy( aContentUri );
    TInt ret = GetElmIdByNameAndValL( KRes16, uriPtr );
    if ( ret >= 0 )
        {
        HBufC8* attrVal;
        GetAttrValueByNameL( ret, KprotocolInfo, attrVal );
        CleanupStack::PushL( attrVal );
        aProtocolInfo = CUpnpDlnaProtocolInfo::NewL( *attrVal );
        CleanupStack::PopAndDestroy( attrVal );
        }
    CleanupStack::PopAndDestroy( uri );

    if ( ret >= 0 ) //item ID number is't meaningfull for function caller
        {
        ret = KErrNone;
        }
    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetElmIdByNameAndValL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::GetElmIdByNameAndValL( const TDesC& aElmName,
    const TDesC& aElmValue )
    {
    TInt ret = KErrNotFound;

    RDbView view = iMetadataStorage->GetViewOfElmIdByNameAndValL( aElmName,
        aElmValue );
    CleanupClosePushL( view );

    // col id
    CDbColSet* colSet = view.ColSetL( );
    const TInt colNo = colSet->ColNo( KElmIdColName );
    delete colSet;

    while ( view.NextL( ) )
        {
        view.GetL( );
        ret = view.ColInt( colNo );
        }

    // clean up
    CleanupStack::PopAndDestroy( &view );

    return ret;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetAttrValueByNameL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//

HBufC8* CUpnpContentDirectoryDb::GetProtocolInfoByImportUriL(
    const TDesC8& aImportUri )
    {
    CUpnpAttributeBean* attBean =
            GetAttrByValueL( KImportUri8( ), aImportUri );
    CleanupStack::PushL( attBean );

    HBufC8* attValBuf = NULL;
    if ( attBean )
        {
        //KprotocolInfo
        CUpnpAttributeBean* att = GetAttrByNameL( attBean->AtrElmId( ),
            KprotocolInfo( ), attValBuf );
        delete att;
        }
    CleanupStack::PopAndDestroy( attBean );
    return attValBuf;
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetAttrValueByNameL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::GetAttrValueByNameL( TInt aElmId,
    const TDesC8& aAttrName, HBufC8*& aAttrVal )
    {
    CUpnpAttributeBean* atrBean =
            GetAttrByNameL( aElmId, aAttrName, aAttrVal );
    delete atrBean;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetAttrByValueL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
CUpnpAttributeBean* CUpnpContentDirectoryDb::GetAttrByValueL(
    const TDesC8& aAttrName, const TDesC8& aAttrVal )
    {
    RDbView view = iMetadataStorage->GetAttrViewL( aAttrName, aAttrVal );
    CleanupClosePushL( view );

    TInt number = view.CountL( );

    CDbColSet* colSet = view.ColSetL( );
    const TInt colNo = colSet->ColNo( KAtrValueColName );
    delete colSet;

    CUpnpAttributeBean* atrBean = NULL;
    if ( view.FirstL( ) )
        {
        view.GetL( );
        atrBean = CUpnpAttributeBean::NewLC( view );
        CleanupStack::Pop( atrBean );
        }
    else
        {
        User::Leave( KErrNotFound );
        }

    // clean up
    CleanupStack::PopAndDestroy( &view );
    return atrBean;
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetAttrByNameL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
CUpnpAttributeBean* CUpnpContentDirectoryDb::GetAttrByNameL( TInt aElmId,
    const TDesC8& aAttrName, HBufC8*& aAttrVal )
    {
    RDbView view = iMetadataStorage->GetAttrViewL( aElmId, aAttrName );
    CleanupClosePushL( view );

    CDbColSet* colSet = view.ColSetL( );
    const TInt colNo = colSet->ColNo( KAtrValueColName );
    delete colSet;

    CUpnpAttributeBean* atrBean = NULL;
    if ( view.FirstL( ) )
        {
        view.GetL( );
        atrBean = CUpnpAttributeBean::NewLC( view );
        aAttrVal = atrBean->AtrValue().AllocL( );
        CleanupStack::Pop( atrBean );
        }
    else
        {
        User::Leave( KErrNotFound );
        }

    // clean up
    CleanupStack::PopAndDestroy( &view );
    return atrBean;
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetObjectIdByElementValueLikeL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::GetObjectIdByElementValueLikeL(
    const TDesC8& aElmName, const TDesC8& aElmValue )
    {
    // convert to unicode
    HBufC* elmName = UpnpCdUtils::Des8ToDesLC( aElmName );
    HBufC* elmValue = UpnpCdUtils::Des8ToDesLC( aElmValue );

    // prepare query buf
    HBufC* query = HBufC::NewLC( KSelectFromSqlCmd().Length( ) + KElmObjIdColName().Length( ) + KElementTableName().Length( ) + KElmNameColName().Length( ) + elmName->Length( ) + KAndLikeConditionSqlCmd().Length( ) + KElmValueColName().Length( ) + elmValue->Length( ) );

    TPtr queryPtr(query->Des( ));

    // format query
    queryPtr.Format( KSelectFromSqlCmd, &KElmObjIdColName,
        &KElementTableName, &KElmNameColName, elmName );
    queryPtr.AppendFormat( KAndLikeConditionSqlCmd, &KElmValueColName,
        elmValue );

    // prepare
    RDbView view = iMetadataStorage->GetEvaluatedViewL( queryPtr );
    CleanupClosePushL( view );

    // col no
    CDbColSet* colSet = view.ColSetL( );
    const TInt colNo = colSet->ColNo( KElmObjIdColName );
    delete colSet;

    TInt ret = KErrNotFound;

    // for one row
    if ( view.NextL( ) )
        {
        view.GetL( );
        if ( !view.IsColNull( colNo ) )
            {
            ret = view.ColInt( colNo );
            }
        }

    CleanupStack::PopAndDestroy( &view );
    CleanupStack::PopAndDestroy( query );

    CleanupStack::PopAndDestroy( elmValue );
    CleanupStack::PopAndDestroy( elmName );

    return ret;
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetParentIdL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::GetParentIdL( TInt aObjectId )
    {
    return iMetadataStorage->GetParentIdL( aObjectId );
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::AddAttributeL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//

void CUpnpContentDirectoryDb::AddAttributeL( TXmlEngElement aElement,
    const TDesC8& aAttrName, TInt aElementId, TInt aObjectId )
    {
    RDbView view = iMetadataStorage->GetAttrViewByObjectIdL( aObjectId,
        aAttrName );
    CleanupClosePushL( view );

    CDbColSet* colSet = view.ColSetL( );
    const TInt elmIdColNo = colSet->ColNo( KAtrElmIdColName );
    delete colSet;

    // Iterate through attributes - usually there is only one
    while ( view.NextL( ) )
        {
        view.GetL( );

        if ( view.ColInt( elmIdColNo ) == aElementId )
            {
            CUpnpAttributeBean* atr = CUpnpAttributeBean::NewLC( view );
            atr->AttachAttrL( aElement );
            CleanupStack::PopAndDestroy( atr );
            }
        }

    // clean up
    CleanupStack::PopAndDestroy( &view );
    }
// -----------------------------------------------------------------------------
// CContentDirectoryDb::CanBeNestedL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TBool CUpnpContentDirectoryDb::CanBeNestedL( TInt aObjId )
    {
    // get path
    HBufC8* path = iMetadataStorage->GetObjectPathL( aObjId );
    CleanupStack::PushL( path );

    // count levels
    TPtrC8 rest(path->Des( ));
    TInt levelNo(1); // root level
    TInt pos( KErrNotFound );
    while ( (pos = rest.Find( KPathSeparator8 )) != KErrNotFound )
        {
        rest.Set( rest.Mid( pos + KPathSeparator8().Length( ) ) );
        levelNo++;
        }
    // clean up
    CleanupStack::PopAndDestroy( path );
    return (levelNo < KAvailableNestedLevels);
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::AddResourceL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::AddResourceL( CUpnpResourcesBean* aResBean,
    TInt aObjId )
    {
    iMetadataStorage->AddResourceL( aResBean, aObjId );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::DeleteResourceL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::DeleteResourceByResIdL( TInt64 aResId,
    TBool aDeleteFile )
    {
    iMetadataStorage->DeleteResourceByResIdL( aResId, aDeleteFile );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::DeleteResourceL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::DeleteResourceByObjIdL( TInt aObjId,
    TBool aDeleteFile )
    {
    iMetadataStorage->DeleteResourceByObjIdL( aObjId, aDeleteFile );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetResourceL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
CUpnpResourcesBean* CUpnpContentDirectoryDb::GetResourceL( TInt64 aResId )
    {
    return iMetadataStorage->GetResourceL( aResId );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::ResNewDownloadDirectoryL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::NewMediaDirectoryL( const TDesC& aOldDir )
    {
    //UPDATE resources SET rsc_readonly=1 WHERE KRscPathColName LIKE $aOldDir* AND KRscReadonlyColName=0
    // prepare command
    HBufC* query = HBufC::NewLC( KNewMediaDirSqlCmd().Length( ) + aOldDir.Length( ) );
    TPtr SqlCmd(query->Des( ));
    SqlCmd.Format( KNewMediaDirSqlCmd, &aOldDir );

    // execute
    iMetadataStorage->ExecuteL( SqlCmd );

    CleanupStack::PopAndDestroy( query );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::GetKeyForUriL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::GetKeyForUriL()
    {
    return iMetadataStorage->GetNextKeyL( KImportUriId8 );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::CheckDatabaseL
// -----------------------------------------------------------------------------
//
void CUpnpContentDirectoryDb::CheckDatabaseL()
    {
    iMetadataStorage->CheckDatabaseL( );
    }
// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::RecreateDatabaseFile
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::RecreateDatabaseFile()
    {
    TRAPD( err, iMetadataStorage->RecreateDatabaseFileL( ) );
    return err;
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::HandleDbError
// -----------------------------------------------------------------------------
//
TInt CUpnpContentDirectoryDb::HandleDbError( TInt aError )
    {
    return iMetadataStorage->HandleDbError( aError );
    }

// -----------------------------------------------------------------------------
// CUpnpContentDirectoryDb::IsDbCreated
// -----------------------------------------------------------------------------
//
TBool CUpnpContentDirectoryDb::IsDbCreated()
    {
    return iMetadataStorage->IsDbCreated( );
    }

//  End of File