/*
* Copyright (c) 2005 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:      
*
*/








// INCLUDE FILES
#include "SenXmlElement.h"
#include "SenXmlConstants.h" // KSenColon, ++
#include "SenXmlUtils.h"

#include "senxmldebug.h"

#ifdef SYMBIAN_SECURE_ECOM 
    // for 2.8, 3.0 or newer:
    #include <xml/attribute.h> // needed for RAttributeArray
#else // for 2.6 or older
    #include "Attribute.h"
#endif

using namespace Xml;

namespace
    {

//    _LIT8(KColon,            ":");
//    _LIT8(KLessThan,         "<");
//    _LIT8(KGreaterThan,      ">");
//    _LIT8(KSpace,            " ");
//    _LIT8(KDblQuot,          "\"");
//    _LIT8(KEqualsDblQuot,    "=\"");
//    _LIT8(KSlashGreaterThan, "/>");
//    _LIT8(KSenLessThanSlash,    "</");
//    _LIT8(KSpaceXmlns,       " xmlns");
//    _LIT8(KXmlns,            "xmlns");
//    _LIT8(KXmlNsAttNamePlusColon, "xmlns:"); 
    const TInt KFlatBufSize        = 64;
    }


EXPORT_C CSenXmlElement* CSenXmlElement::NewL(const TDesC8& aLocalName)
    {
    CSenXmlElement* pNew = new (ELeave) CSenXmlElement;
    CleanupStack::PushL(pNew);
    pNew->BaseConstructL(aLocalName);
    CleanupStack::Pop(); // pNew;
    return pNew;
    }

EXPORT_C CSenXmlElement* CSenXmlElement::NewL(const TDesC8& aNsUri,
                                                const TDesC8& aLocalName)
    {
    CSenXmlElement* pNew = new (ELeave) CSenXmlElement;
    CleanupStack::PushL(pNew);
    pNew->BaseConstructL(aNsUri, aLocalName);
    CleanupStack::Pop(); // pNew;
    return pNew;
    }

EXPORT_C CSenXmlElement* CSenXmlElement::NewL(const TDesC8& aNsUri,
                                                const TDesC8& aLocalName,
                                                const TDesC8& aQName)
    {
    CSenXmlElement* pNew = new (ELeave) CSenXmlElement;
    CleanupStack::PushL(pNew);
    pNew->BaseConstructL(aNsUri, aLocalName, aQName);
    CleanupStack::Pop(); // pNew;
    return pNew;
    }

EXPORT_C CSenXmlElement* CSenXmlElement::NewL(const TDesC8& aNsUri,
                                                const TDesC8& aLocalName,
                                                const TDesC8& aQName,
                                                const RAttributeArray& apAttrs)
    {
    CSenXmlElement* pNew = new (ELeave) CSenXmlElement;
    CleanupStack::PushL(pNew);
    pNew->BaseConstructL(aNsUri, aLocalName, aQName, apAttrs);
    CleanupStack::Pop(); // pNew;
    return pNew;
    }


EXPORT_C CSenXmlElement* CSenXmlElement::NewL(const TDesC8& aNsUri,
                                                const TDesC8& aLocalName,
                                                const TDesC8& aQName,
                                                const RAttributeArray& apAttrs,
                                                CSenElement& aParent)
    {
    CSenXmlElement* pNew = new (ELeave) CSenXmlElement;
    CleanupStack::PushL(pNew);
    pNew->BaseConstructL(aNsUri, aLocalName, aQName, apAttrs, aParent);
    CleanupStack::Pop(); // pNew;
    return pNew;
    }

EXPORT_C CSenXmlElement::CSenXmlElement()
: ipLocalName(NULL),
  ipContentBuf(NULL),
  ipParent(NULL),
  ipNamespace(NULL)
    {
    }

EXPORT_C CSenXmlElement::~CSenXmlElement()
    {
    if(ipContentWriteStream)
        {
        ipContentWriteStream->Close();
        delete ipContentWriteStream;
        ipContentWriteStream = NULL;
        }

    if(ipAttrs)
        {
        ipAttrs->ResetAndDestroy();
        delete ipAttrs;
        ipAttrs = NULL; // not required
        }
    if(ipElements)
        {
        ipElements->ResetAndDestroy();
        delete ipElements;
        ipElements = NULL; // not required
        }
    if(ipNamespaces)
        {
        ipNamespaces->ResetAndDestroy();
        delete ipNamespaces;
        ipNamespaces = NULL; // not required
        }

    delete ipLocalName;
    ipLocalName = NULL; // not required


    delete ipContentBuf;
    ipContentBuf = NULL; // not required

    }



EXPORT_C void CSenXmlElement::BaseConstructL(const TDesC8& aLocalName)
    {
    SenXmlUtils::LeaveOnInvalidElementNameL(aLocalName);
    ipLocalName = aLocalName.AllocL();
    }

EXPORT_C void CSenXmlElement::BaseConstructL(const TDesC8& aNsUri,
                                              const TDesC8& aLocalName)
    {
    SenXmlUtils::LeaveOnInvalidElementNameL(aLocalName);
    if(aNsUri.Length()>0)
        {
        SetNamespaceL(aNsUri);
        }
    ipLocalName = aLocalName.AllocL();
    }

EXPORT_C void CSenXmlElement::BaseConstructL(   const TDesC8& aNsUri,
                                                const TDesC8& aLocalName,
                                                const TDesC8& aQName)
    {
    SenXmlUtils::LeaveOnInvalidElementNameL(aLocalName);
    SenXmlUtils::LeaveOnInvalidElementNameL(aQName);
    ipLocalName = aLocalName.AllocL();
    TPtrC8 ptrPrefix(KNullDesC8);

    if (aQName.Length() > 0 )
        {
        TInt colon = aQName.Locate(':');
        if (colon > 0) // Note: 0 also treated as no prefix
            {
            ptrPrefix.Set(aQName.Ptr(),colon);
            }
        }

    SetNamespaceL(ptrPrefix, aNsUri);
    }

EXPORT_C void CSenXmlElement::BaseConstructL(   const TDesC8& aNsUri,
                                                const TDesC8& aLocalName,
                                                const TDesC8& aQName,
                                                const RAttributeArray& apAttrs)
    {
    BaseConstructL(aNsUri, aLocalName, aQName);
    SetAttributesL(apAttrs);
    }

EXPORT_C void CSenXmlElement::BaseConstructL(   const TDesC8& aNsUri,
                                                const TDesC8& aLocalName,
                                                const TDesC8& aQName,
                                                const RAttributeArray& apAttrs,
                                                CSenElement& aParent    )
    {
    // parent must be set here at first line, because
    // namespace setting dependends of it(!)
    ipParent = &aParent;

    BaseConstructL(aNsUri, aLocalName, aQName);
    SetAttributesL(apAttrs);
    }

EXPORT_C void CSenXmlElement::SetAttributesL(const RAttributeArray& apAttrs)
    {
    AddAttributesL(apAttrs);
    }


EXPORT_C void CSenXmlElement::AddAttributesL(const RAttributeArray& apAttrs)
    {
    TInt count(apAttrs.Count());
    HBufC8* qName = NULL;
    for(TInt i=0; i<count; i++)
        {
        const TPtrC8 localname = apAttrs[i].Attribute().LocalName().DesC();
        const TPtrC8 prefix = apAttrs[i].Attribute().Prefix().DesC();
        const TPtrC8 value = apAttrs[i].Value().DesC();

        SenXmlUtils::BuildQNameL(prefix, localname, qName);

        CleanupStack::PushL(qName);

        HBufC8* encodedValue = NULL;
        TBool encoded = 
            SenXmlUtils::EncodeHttpCharactersL(value, encodedValue);

        if (encoded)
            {
            CleanupStack::PushL(encodedValue);
            AddAttributeL(*qName, localname, *encodedValue); 
            // note: this intentionally
            // skips the 2 param 
            // AddAttributeL() function

            CleanupStack::PopAndDestroy(); // encodedValue
            }
        else
            {
            AddAttributeL(*qName, localname, value); 
            // note: this intentionally
            // skips the 2 param 
            // AddAttributeL() function
            }

        CleanupStack::PopAndDestroy(); // qName
        }
    }

EXPORT_C const TDesC8& CSenXmlElement::AddAttributeL(const TDesC8& aQName,
                                                     const TDesC8& aLocalName,
                                                     const TDesC8& aValue)
    {
    const TDesC8& nsPrefix = NsPrefix();
    // first we check for possible namespace declarations...
    if ( aQName == KSenXmlns || aLocalName == KSenXmlns)
        {
            if(nsPrefix.Length() > 0)
                {
                // we have an additional namespace declaration
                AddNamespaceL(KNullDesC8(), aValue);
                }
            else
                {
                // this is a default name space declaration
                SetNamespaceL(aValue);                
                }
                
        }
    else if (aLocalName == NsPrefix())
        {
        //we have a new declaration for the namespace of this element
        SetNamespaceL(aLocalName, aValue);
        }
    else if(aQName.Find(KSenXmlNsAttNamePlusColon) == 0)
        {
        // we have an additional namespace declaration
        AddNamespaceL(aLocalName, aValue);
        }
    else
        {
        // we have a real attribute!
        CSenBaseAttribute* pAttribute = CSenBaseAttribute::NewL(aQName,
                                                                aLocalName,
                                                                aValue);
        AddAttributeL(pAttribute);
        }
    return aValue;
    }



EXPORT_C const TDesC8& CSenXmlElement::AddAttributeL(const TDesC8& aAttrName,
                                                      const TDesC8& aValue)
    {
    // note, the aAttrName may be a qualified name or simply localname
    // strip off the possible prefix from possible qualified name:
    TPtrC8 localName = SenXmlUtils::LocalName(aAttrName);
    return AddAttributeL(aAttrName, localName, aValue);
    }

// takes the ownership of aAttribute
EXPORT_C const TDesC8& CSenXmlElement::AddAttributeL(
                                                CSenBaseAttribute* apAttribute)
    {
    CSenBaseAttribute* pOldAtt = FindAttr(apAttribute->Name());

    // if attribute array is not yet allocated, instantiate here
    if(!ipAttrs)
        {
        ipAttrs = new (ELeave) RPointerArray<CSenBaseAttribute>;
        }

    if(!pOldAtt)
        {
        // transfrer the ownership to this class:
        ipAttrs->Append(apAttribute);
        return apAttribute->Value();
        }
    else
        {
        pOldAtt->SetValueL(apAttribute->Value());
        delete apAttribute;
        apAttribute = NULL;
        return pOldAtt->Value();
        }
    }


EXPORT_C CSenBaseAttribute* CSenXmlElement::FindAttr(const TDesC8& aName)
    {
    if(ipAttrs)
        {
        TInt count(ipAttrs->Count());
        for (TInt i = 0; i < count; i++)
            {
            CSenBaseAttribute* pAttribute = (*ipAttrs)[i];
            if(pAttribute->Name() == aName)
                {
                return pAttribute;
                }
            }
        }
    return NULL;
    }


EXPORT_C TInt CSenXmlElement::IndexOfElement(const TDesC8& aNsUri,
                                              const TDesC8& aLocalName) const
    {
    if(ipElements)
        {
        TInt count(ipElements->Count());

        for (TInt i = 0; i < count; i++)
            {
            CSenElement* pElement = (*ipElements)[i];
            const TDesC8& nsUri = pElement->NamespaceURI();
            const TDesC8& localName = pElement->LocalName();
            if ((nsUri == aNsUri) && (localName == aLocalName))
                {
                return i;
                }
            }
        }
    return KErrNotFound;
    }

EXPORT_C const TDesC8& CSenXmlElement::LocalName() const
    {
    if (ipLocalName == NULL)
        {
        return KNullDesC8();
        }
    else
        {
        return *ipLocalName;
        }
    }

EXPORT_C const TDesC8& CSenXmlElement::NamespaceURI() const
    {
    if (ipNamespace)
        {
        return ipNamespace->URI();
        }
    else
        {
        return KNullDesC8();
        }
    }

EXPORT_C const TDesC8& CSenXmlElement::NsPrefix() const
    {
    if (!ipNamespace)
        {
        return KNullDesC8();
        }
    else
        {
        return ipNamespace->Prefix();
        }
    }

EXPORT_C TBool CSenXmlElement::HasContent() const
    {
    if (!ipContentBuf)
        {
        return EFalse;
        }
    else
        {
        return (ipContentBuf->Size() > 0);
        }
    }

EXPORT_C TPtrC8 CSenXmlElement::Content() const
    {
    if (!ipContentBuf)
        {
        return KNullDesC8();
        }
    else
        {
        TPtrC8 p8 = ipContentBuf->Ptr(0);
        return p8;
        }
    }


EXPORT_C HBufC* CSenXmlElement::ContentUnicodeL() const
    {
    HBufC* pRet = SenXmlUtils::ToUnicodeLC(Content());
    CleanupStack::Pop(); // pRet;
    return pRet;
    }

EXPORT_C TPtrC8 CSenXmlElement::SetContentL(const TDesC8& aContent)
    {
    AllocContentBufL(); 
	if(ipContentBuf)
		{
		ipContentBuf->ResizeL(aContent.Size());
		ipContentBuf->Write(0,
        TPtrC8(REINTERPRET_CAST(const TUint8*, aContent.Ptr()),
                                                aContent.Size())
        );
		if(ipContentWriteStream)
			{
			// Reset stream
			ipContentWriteStream->Open(*ipContentBuf);
			}
		}
    return Content();
    }

EXPORT_C RWriteStream& CSenXmlElement::ContentWriteStreamL()
    {
    AllocContentBufL();
    // Allocate stream
    if(!ipContentWriteStream)
        {
        ipContentWriteStream = new (ELeave) RBufWriteStream;
        }
    ipContentWriteStream->Open(*ipContentBuf);
    return *ipContentWriteStream;
    }

// NOTE: assumes(!) that the namespace is same(!)
// Optimization 
// RPointerArray<CSenElement>* or NULL if no elements have been added!
EXPORT_C RPointerArray<CSenElement>& CSenXmlElement::ElementsL()
    {
    if(!ipElements)
        {
        ipElements = new (ELeave) RPointerArray<CSenElement>;
        }
    return *ipElements;
    }

EXPORT_C TInt CSenXmlElement::ElementsL(
                                    RPointerArray<CSenElement>& aElementArray,
                                    const TDesC8& aNsUri,
                                    const TDesC8& aLocalName)
    {
    TInt retVal(KErrNotFound);

    if(ipElements) // return KErrNotFound, if zero elements have been added
        {
        TInt count(ipElements->Count());

        if (count > 0) // return KErrNotFound, if owned array is empty
            {
            CSenElement* pElement = NULL;
            for (TInt i=0; i<count; i++)
                {
                pElement = (*ipElements)[i];
                if ( (aLocalName == pElement->LocalName()) &&
                     (aNsUri == pElement->NamespaceURI()) )
                    {
                    aElementArray.Append(pElement);
                    }
                }
            retVal = KErrNone;
            }
        }
    return retVal;
    }

EXPORT_C TInt CSenXmlElement::ElementsL(
                                    RPointerArray<CSenElement>& aElementArray,
                                    const TDesC8& aLocalName)
    {
    return ElementsL(aElementArray,NamespaceURI(),aLocalName);
    }

EXPORT_C const TDesC8* CSenXmlElement::AttrValue(const TDesC8& aName)
    {
    CSenBaseAttribute* pAttr = FindAttr(aName);
    if (pAttr == NULL)
        {
        return NULL;
        }
    else
        {
        return &(pAttr->Value());
        }
    }

EXPORT_C void CSenXmlElement::AddAttrL(const TDesC8& aName,
                                        const TDesC8& aValue)
    {
    CSenBaseAttribute* pAttr = FindAttr(aName);
    if (!pAttr)
        {
        if(!ipAttrs)
            {
            ipAttrs = new (ELeave) RPointerArray<CSenBaseAttribute>;
            }

        User::LeaveIfError(ipAttrs->Append(CSenBaseAttribute::NewL(aName,
                                                                 aValue)));
        }
    else
        {
        pAttr->SetValueL(aValue);
        }
    }

EXPORT_C CSenElement* CSenXmlElement::Parent()
    {
    return ipParent;
    }

EXPORT_C CSenElement* CSenXmlElement::SetParent(CSenElement* apParent)  // IOP
    {
    if (apParent && ipParent != apParent)
        {
        ipParent = apParent;
        if (!ipNamespace)
            {
            // check if there is a default namespace declared in the scope of
            // the parent
            const CSenNamespace* pParentNamespace =
                ((CSenXmlElement*) ipParent)->Namespace(KNullDesC8,ETrue);
            if (pParentNamespace && pParentNamespace->Prefix() == KNullDesC8)
                {
                ipNamespace = (CSenNamespace*)pParentNamespace;
                }
            }
        else
            {
            //check if the parent already has a namespace for this element
            // if so remove it from the local namespace table
            const CSenNamespace* pNs = ipParent->Namespace(KNullDesC8,
                                                    ipNamespace->URI());
            if (pNs && pNs != ipNamespace)
                {
                if (ipNamespace->Compare(*pNs) ||
                    ipNamespace->Prefix().Length() == 0)
                    {
                    //prefix is also identical or this element has no prefix
                    if(ipNamespaces)
                        {
                        TInt nsIndex = ipNamespaces->Find(ipNamespace);
                        if ( nsIndex != KErrNotFound)
                            {
                            ipNamespaces->Remove(nsIndex);
                            }
                        }
                    delete ipNamespace;
                    ipNamespace = NULL;
                    ipNamespace = (CSenNamespace*)pNs;
                    }
                }

            }
        }
    return apParent;
    }

EXPORT_C MSenElement& CSenXmlElement::Root()
    {
    if (ipParent == NULL)
        {
        return *this;
        }
    else
        {
        return ipParent->Root();
        }
    }

EXPORT_C CSenElement* CSenXmlElement::Element(const TDesC8& aLocalName)
    {
    return Element(NamespaceURI(), aLocalName);
    }

EXPORT_C CSenElement* CSenXmlElement::Element(const TDesC8& aNsUri,
                                               const TDesC8& aLocalName)
    {
    if(ipElements)
        {
        TInt idx = IndexOfElement(aNsUri, aLocalName);
        if (idx < 0)
            {
            return NULL;
            }
        else
            {
            return (*ipElements)[idx];
            }
        }
    return NULL;
    }

EXPORT_C CSenElement* CSenXmlElement::CreateElementL(const TDesC8& aNsPrefix,
                                                      const TDesC8& aLocalName)
    {
    CSenElement* pNewElement = NULL;

    if (aNsPrefix.Length() > 0)
        {
        CSenNamespace* pNamespace = (CSenNamespace*)Namespace(aNsPrefix);
        if (pNamespace)
            {
            HBufC8 *pQName =
                HBufC8::NewLC(aNsPrefix.Length() + aLocalName.Length() +5);
            TPtr8 ptr = pQName->Des();
            ptr.Append(aNsPrefix);
            ptr.Append(':');
            ptr.Append(aLocalName);
            pNewElement =
                CSenXmlElement::NewL(pNamespace->URI(), aLocalName, *pQName);
            CleanupStack::PopAndDestroy(); // pQName
            }
        }
    else
        {
        pNewElement = CSenXmlElement::NewL(aLocalName);
        }

    return pNewElement; // Returns NULL if required namespace can not be found!
    }

EXPORT_C CSenElement& CSenXmlElement::InsertElementL(
                                          CSenElement& aElement,
                                          const CSenElement& aBeforeElement)
    {
    TInt index(KErrNotFound);

    // allocate element array, if not already reserved
    if(!ipElements)
        {
        ipElements = new (ELeave) RPointerArray<CSenElement>;
        }
    else
        {
        // search only if array is not brand new
        index = ipElements->Find(&aBeforeElement);
        }

    if (index != KErrNotFound)
        {
        // repleace element
        User::LeaveIfError(ipElements->Insert(&aElement,index));
        }
    else
        {
        // add new element
        User::LeaveIfError(ipElements->Append(&aElement));
        }
    aElement.SetParent(this);
    return aElement;
    }

EXPORT_C CSenElement& CSenXmlElement::AddElementL(CSenElement& aElement)
    {
    // allocate element array, if not already reserved
    if(!ipElements)
        {
        ipElements = new (ELeave) RPointerArray<CSenElement>;
        }

    User::LeaveIfError(ipElements->Append(&aElement));
    aElement.SetParent(this);
    return aElement;
    }

EXPORT_C CSenElement& CSenXmlElement::AddElementL(const TDesC8& aNsUri,
                                                   const TDesC8& aLocalName)
    {
    return AddElementL(*CSenXmlElement::NewL(aNsUri, aLocalName));
    }

EXPORT_C CSenElement& CSenXmlElement::AddElementL(
    const TDesC8& aNsUri,
    const TDesC8& aLocalName,
    const TDesC8& aQName
    )
    {
    return AddElementL(*CSenXmlElement::NewL(aNsUri, aLocalName, aQName));
    }

EXPORT_C CSenElement& CSenXmlElement::AddElementL(const TDesC8& aLocalName)
    {
    return AddElementL(*CSenXmlElement::NewL(aLocalName));
    }

EXPORT_C CSenElement* CSenXmlElement::RemoveElement(CSenElement& aElement)
    {
    CSenElement* pElement = NULL;

    if(ipElements)
        {
        TInt idx = ipElements->Find(&aElement);
        if (idx >= 0)
            {
            pElement = (*ipElements)[idx];
            ipElements->Remove(idx);
            pElement->SetParent(NULL);
            return pElement;
            }
        }
    return pElement;
    }

EXPORT_C CSenElement* CSenXmlElement::RemoveElement(const TDesC8& aNsUri,
                                                     const TDesC8& aLocalName)
    {
    TInt idx = IndexOfElement(aNsUri, aLocalName);
    if (idx >= 0)
        {
        CSenElement* pElement = (*ipElements)[idx];
        ipElements->Remove(idx);
        pElement->SetParent(NULL);
        return pElement;
        }
    else
        {
        return NULL;
        }
    }

EXPORT_C CSenElement* CSenXmlElement::RemoveElement(const TDesC8& aLocalName)
    {
    return RemoveElement(NamespaceURI(), aLocalName);
    }

EXPORT_C CSenElement* CSenXmlElement::ReplaceElementL(CSenElement& aElement)
    {
    CSenElement* pOldElement =
                RemoveElement(aElement.NamespaceURI(), aElement.LocalName());
    CleanupStack::PushL(pOldElement);
    AddElementL(aElement);
    CleanupStack::Pop(); // pOldElement;
    return pOldElement;
    }

EXPORT_C HBufC8* CSenXmlElement::AsXmlL()
    {
    CBufFlat *pBuf = CBufFlat::NewL(KFlatBufSize);
    CleanupStack::PushL(pBuf);
    TPtrC8 p = WriteToBufL(*pBuf);
    HBufC8* pRet = p.AllocL();
    CleanupStack::PopAndDestroy(); // pBuf;
    return pRet;

    }

EXPORT_C HBufC* CSenXmlElement::AsXmlUnicodeL()
    {
    CBufFlat *pBuf = CBufFlat::NewL(KFlatBufSize);
    CleanupStack::PushL(pBuf);
    TPtrC8 p8 = WriteToBufL(*pBuf);
    HBufC* pRet = SenXmlUtils::ToUnicodeLC(p8);
    CleanupStack::Pop(); // pop pRet;
    CleanupStack::PopAndDestroy(); // pBuf;
    return pRet;
    }

EXPORT_C TPtrC8 CSenXmlElement::WriteToBufL(CBufBase& aBuf)
    {
    RBufWriteStream bufWs(aBuf);
    CleanupClosePushL(bufWs);
    this->WriteAsXMLToL(bufWs);
    CleanupStack::PopAndDestroy(); // bufWs.Close();
    return aBuf.Ptr(0);
    }

EXPORT_C void CSenXmlElement::WriteAsXMLToL(RWriteStream& aWriteStream)
    {
    // Find out whether we should declare the namespace
    TPtrC8 nsPrefix = NsPrefix();

    // Element name
    aWriteStream.WriteL(KSenLessThan);
    if (nsPrefix.Length() > 0)
        {
        aWriteStream.WriteL(nsPrefix);
        aWriteStream.WriteL(KSenColon);
        }
    aWriteStream.WriteL(*ipLocalName);

    
    if ((ipAttrs && ipAttrs->Count() > 0) || 
         (ipNamespaces && ipNamespaces->Count() > 0))
        {
        WriteNamespacesToL(aWriteStream);
        WriteAttrsToL(aWriteStream);
        }

    // Elements and content
    if ((ipElements && ipElements->Count() > 0) || HasContent())
        {
        aWriteStream.WriteL(KSenGreaterThan);

        // Body
        WriteElementsToL(aWriteStream);
        WriteContentToL(aWriteStream);

        // Closing element
        aWriteStream.WriteL(KSenLessThanSlash);
        if (nsPrefix.Length() > 0)
            {
            aWriteStream.WriteL(nsPrefix);
            aWriteStream.WriteL(KSenColon);
            }
        aWriteStream.WriteL(*ipLocalName);
        aWriteStream.WriteL(KSenGreaterThan);
        }
    else
        {
        aWriteStream.WriteL(KSenSlashGreaterThan);
        }
    }

EXPORT_C void CSenXmlElement::WriteAttrToL(RWriteStream& aWriteStream,
                                            const TDesC8& aName,
                                            const TDesC8& aValue)
    {
    aWriteStream.WriteL(KSenSpace);
    aWriteStream.WriteL(aName);
    aWriteStream.WriteL(KSenEqualsDblQuot);
    aWriteStream.WriteL(aValue);
    aWriteStream.WriteL(KSenDblQuot);
    }

EXPORT_C void CSenXmlElement::WriteAttrsToL(RWriteStream& aWriteStream)
    {
    if(ipAttrs)
        {
        TInt count(ipAttrs->Count());
        for (TInt i = 0; i < count; i++)
            {
            WriteAttrToL(aWriteStream, (*ipAttrs)[i]->Name(), (*ipAttrs)[i]->Value());
            }
        }
    }

EXPORT_C void CSenXmlElement::WriteNamespacesToL(RWriteStream& aWriteStream)
    {
    if(ipNamespaces)
        {
        CSenNamespace* ns = NULL;
        TInt count = ipNamespaces->Count();
        for (TInt i=0; i < count; i++)
            {
            ns = (*ipNamespaces)[i];
            if (ns)
                {
                aWriteStream.WriteL(KSenSpaceXmlns);
                if (ns->Prefix().Length() > 0)
                    {
                    aWriteStream.WriteL(KSenColon);
                    aWriteStream.WriteL(ns->Prefix());
                    }
                aWriteStream.WriteL(KSenEqualsDblQuot);
                aWriteStream.WriteL(ns->URI());
                aWriteStream.WriteL(KSenDblQuot);
                }
            }
        }
    }

EXPORT_C void CSenXmlElement::WriteElementsToL(RWriteStream& aWriteStream)
    {
    if(ipElements)
        {
        TInt elementCount(ipElements->Count());
        for (TInt i=0; i<elementCount; i++)
            {
            (*ipElements)[i]->WriteAsXMLToL(aWriteStream);
            }
        }
    }

EXPORT_C void CSenXmlElement::WriteContentToL(RWriteStream& aWriteStream)
    {
    aWriteStream.WriteL(Content());
    }

EXPORT_C MSenElement* CSenXmlElement::AsElement()
    {
    return this;
    }

EXPORT_C void CSenXmlElement::AllocContentBufL()
    {
    if(!ipContentBuf)
        {
        ipContentBuf = CBufFlat::NewL(KFlatBufSize);
        }
    }

EXPORT_C void CSenXmlElement::SetNamespaceL(const TDesC8& aNsUri)
    {
    SetNamespaceL(KNullDesC8, aNsUri);
    }

EXPORT_C void CSenXmlElement::SetNamespaceL(const TDesC8& aNsPrefix,
                                            const TDesC8& aNsUri)
    {
    if (aNsUri.Length() > 0)
        {
        ipNamespace = (CSenNamespace*) Namespace(aNsPrefix, aNsUri);
        if (!ipNamespace) // not already defined
            {
            // allocate array if not already reserved
            if(!ipNamespaces)
                {
                ipNamespaces = new (ELeave) RPointerArray<CSenNamespace>;
                }
            ipNamespace = CSenNamespace::NewL(aNsPrefix, aNsUri);
            ipNamespaces->Append(ipNamespace);
            }
        }
    }

EXPORT_C const CSenNamespace* CSenXmlElement::Namespace()
    {
    return ipNamespace;
    }

EXPORT_C const CSenNamespace* CSenXmlElement::Namespace(
                                                    const TDesC8& aNsPrefix)
    {
    return Namespace(aNsPrefix,ETrue);
    }

EXPORT_C const CSenNamespace* CSenXmlElement::Namespace(
                                                    const TDesC8& aNsPrefix,
                                                    const TBool aCheckInParent)
    {
    if (aNsPrefix.Length() == 0) return NULL;

    const CSenNamespace* pNamespace = NULL;

    if(ipNamespaces)
        {
        TInt count(ipNamespaces->Count());
        for (TInt i=0; i<count && pNamespace == NULL; i++)
            {
            pNamespace = (*ipNamespaces)[i];
            if (pNamespace)
                {
                if (pNamespace->Prefix() != aNsPrefix) pNamespace = NULL;
                }
            }
        }

    if (pNamespace == NULL && ipParent && aCheckInParent)
        {
        pNamespace = ipParent->Namespace(aNsPrefix);
        }

    return pNamespace;
    }


EXPORT_C const CSenNamespace* CSenXmlElement::Namespace(
                                                    const TDesC8& aNsPrefix,
                                                    const TDesC8& aUri)
    {
    // If prefix == NULL, then any prefix is ok
    const CSenNamespace* pNs = NULL;
    // Check if the namespace has already been declared
    
    if(ipNamespaces)
        {
        TInt count = ipNamespaces->Count();
        if (count != 0)
            {
            for (TInt i=0; i<count; i++)
                {
                pNs = (*ipNamespaces)[i];
                if (!pNs->Compare(aNsPrefix, aUri))
                    {
                    pNs = NULL; // not equal
                    }
                else
                    {
                    break;
                    }
                }
            }
        }
    if (pNs == NULL && ipParent != NULL)
        {
        // if no namespace defined, but there is a parent, ask its namespace
        pNs = ipParent->Namespace(aNsPrefix, aUri);
        }
    return pNs;
    }

EXPORT_C RPointerArray<CSenBaseAttribute>& CSenXmlElement::AttributesL()
    {
    // if attribute array is not yet allocated, instantiate here
    if(!ipAttrs)
        {
        ipAttrs = new (ELeave) RPointerArray<CSenBaseAttribute>;
        }
    return *ipAttrs;
    }

EXPORT_C RPointerArray<CSenNamespace>& CSenXmlElement::NamespacesL()
    {
    if(!ipNamespaces)
        {
        ipNamespaces = new (ELeave) RPointerArray<CSenNamespace>;
        }
    return *ipNamespaces;
    }

EXPORT_C void CSenXmlElement::CopyFromL(CSenElement& aSource)
    {
    TPtrC8 sourceContent = aSource.Content();
    if (sourceContent.Length() > 0)
        {
        if (ipContentBuf == NULL)
            {
            SetContentL(sourceContent);
            }
        else
            {
            RBufWriteStream bufWs(*ipContentBuf);
            CleanupClosePushL(bufWs);
            bufWs.WriteL(sourceContent);
            CleanupStack::PopAndDestroy(); // close bufWs
            }
        }

    RPointerArray<CSenNamespace> sourceNamespaces = aSource.NamespacesL();
    if (sourceNamespaces.Count() > 0)
        {
        for (TInt i=0;i<sourceNamespaces.Count(); i++)
            {
            CSenNamespace* pNamespace = sourceNamespaces[i];
            CSenNamespace* pNewNamespace =
                CSenNamespace::NewL(pNamespace->Prefix(),pNamespace->URI());
            CleanupStack::PushL(pNewNamespace);

            // allocate array if not already reserved
            if(!ipNamespaces)
                {
                ipNamespaces = new (ELeave) RPointerArray<CSenNamespace>;
                }
            
#ifdef EKA2
            ipNamespaces->AppendL(pNewNamespace);
#else
            User::LeaveIfError(ipNamespaces->Append(pNewNamespace));
#endif
            CleanupStack::Pop(pNewNamespace);
            }
        }
    SetNamespaceL(aSource.NsPrefix(), aSource.NamespaceURI());

    RPointerArray<CSenBaseAttribute> sourceAttributes = aSource.AttributesL();
    if (sourceAttributes.Count() > 0)
        {
        for (TInt i=0;i<sourceAttributes.Count(); i++)
            {
            CSenBaseAttribute* pBaseAttribute = sourceAttributes[i];

            // 2005-04-28: check for duplicate and override existing value if
            // attribute already exists.
            CSenBaseAttribute* pOriginal = FindAttr(pBaseAttribute->Name());
            if (pOriginal)
                {
                pOriginal->SetValueL(pBaseAttribute->Value());
                continue;
                }

            CSenBaseAttribute* pNewBaseAttribute =
                CSenBaseAttribute::NewL(pBaseAttribute->Name(),
                                        pBaseAttribute->Value());
            CleanupStack::PushL(pNewBaseAttribute);

            // if attribute array is not yet allocated, instantiate here
            if(!ipAttrs)
                {
                ipAttrs = new (ELeave) RPointerArray<CSenBaseAttribute>;
                }

#ifdef EKA2
            ipAttrs->AppendL(pNewBaseAttribute);
#else
            User::LeaveIfError(ipAttrs->Append(pNewBaseAttribute));
#endif
            CleanupStack::Pop(pNewBaseAttribute);
            }
        }

    RPointerArray<CSenElement> sourceElements = aSource.ElementsL();
    if (sourceElements.Count() > 0)
        {
        for (TInt i=0;i<sourceElements.Count(); i++)
            {
            CSenElement* pElement = sourceElements[i];
            CSenElement* pNewElement =
                                CSenXmlElement::NewL(pElement->LocalName());
            CleanupStack::PushL(pNewElement);
            pNewElement->SetParent(this);
            pNewElement->CopyFromL(*pElement);

            if(!ipElements)
                {
                ipElements = new (ELeave) RPointerArray<CSenElement>;
                }

#ifdef EKA2
            ipElements->AppendL(pNewElement);
#else
            User::LeaveIfError(ipElements->Append(pNewElement));
#endif // EKA2
            CleanupStack::Pop(pNewElement);
            }
        }
    }

EXPORT_C void CSenXmlElement::SetPrefixL(const TDesC8& aPrefix)
    {
    if (ipNamespaces && ipNamespaces->Find(ipNamespace) > -1)
        {
        //the namespace was locally declared

        // Check if element already has a namespace with given prefix
        CSenNamespace* pNamespace = NULL;
        TInt count(ipNamespaces->Count());
        for (TInt i=0; i<count && pNamespace == NULL; i++)
            {
            pNamespace = (*ipNamespaces)[i];
            if (pNamespace->Prefix() != aPrefix) 
                {
                pNamespace = NULL;
                }
            }

        if (pNamespace)
            {
            // Update existing namespace
            const TDesC8& uri = ipNamespace->URI();
            pNamespace->SetUriL(uri);
            ipNamespace = pNamespace;
            }
        else
            {
            if(ipElements) // are there any child elements?
                {
                if (ipNamespace->Prefix().Length() > 0 && ipElements->Count() > 0)
                    {
                    // there may be children that depend on the old prefix in e.g. attribute names
                    const TDesC8& uri = ipNamespace->URI();
                    ipNamespace = CSenNamespace::NewL(aPrefix, uri);

                    // append as new namespace
                    ipNamespaces->Append(ipNamespace);
                    }
                }
            ipNamespace->SetPrefixL(aPrefix);
            }
        }
    }

EXPORT_C const CSenNamespace* CSenXmlElement::AddNamespaceL(
                                                CSenNamespace& aNewNamespace,
                                                TBool aCheckInParent)
    {
    const CSenNamespace* pNamespace =
                        Namespace(aNewNamespace.Prefix(), aCheckInParent);

    if (pNamespace == NULL) // does not exist
        {
        CSenNamespace* pNewNamespace =
            CSenNamespace::NewL(aNewNamespace.Prefix(), aNewNamespace.URI());

        // allocate array if not already reserved
        if(!ipNamespaces)
            {
            ipNamespaces = new (ELeave) RPointerArray<CSenNamespace>;
            }
        ipNamespaces->Append(pNewNamespace);
        pNamespace = pNewNamespace;
        }
    return pNamespace;
    }

EXPORT_C const CSenNamespace* CSenXmlElement::AddNamespaceL(
                                                         const TDesC8& aPrefix,
                                                         const TDesC8& aUri)
    {
    CSenNamespace* pNamespace = (CSenNamespace*)Namespace(aPrefix);
	if (!pNamespace)
		{
		if ( aUri.Length() > 0)
			{
				pNamespace = CSenNamespace::NewL(aPrefix, aUri);
			// allocate array if not already reserved
			if(!ipNamespaces)
				{
				ipNamespaces = new (ELeave) RPointerArray<CSenNamespace>;
				}
			ipNamespaces->Append(pNamespace);
			}
		}
	    else
	        {
	        pNamespace->SetUriL(aUri); // override current namespace URI with new one
	      
			}

    return pNamespace;
    }

EXPORT_C CSenElement* CSenXmlElement::DetachL()
    {
    if (!ipParent)
        {
        return NULL;
        }

    // allocate array if not already reserved
    if(!ipNamespaces)
        {
        ipNamespaces = new (ELeave) RPointerArray<CSenNamespace>;
        }

    AddNamespaceMissingFromL(*ipNamespaces);

    // optimization: if nothing was added, free the array allocation
    if(ipNamespaces && ipNamespaces->Count()==0)
        {
        ipNamespaces->ResetAndDestroy(); 
        delete ipNamespaces; 
                            
        ipNamespaces = NULL;
        }

    ipParent->RemoveElement(*this);
    return this;
    }

void CSenXmlElement::AddNamespaceMissingFromL( RPointerArray<CSenNamespace>& aNamespaces )
    {
    // Add namespace of this element (possibly copied from parent)
    // if that namespace is not already defined in
    // aNamespaces array.
    if ( ipNamespace && aNamespaces.Find(ipNamespace) == -1)
        {
        // Check if namespace declaration of this element can
        // be found from aNamespaces array
        TInt count(aNamespaces.Count());
        TBool found(EFalse);
        TInt i = 0;
        for (; i<count; i++)
            {
            if ( aNamespaces[i]->URI() == ipNamespace->URI() && 
                 aNamespaces[i]->Prefix() == ipNamespace->Prefix() )
                {
                found = ETrue;
                break;
                }
            }
        if ( !found )
            {
            //If not found then check in namespace usage array that namspace declaration 
            //is present or not. If yes then continue without adding.
            if((ipNamespaces && ipNamespaces->Find(ipNamespace) == -1) || !ipNamespaces)
	            {
            
	            // Not found 
	            // => Create a copy of namespace declaration
	            //    and append it into aNamespaces array
	            CSenNamespace* pCopy =
	                    CSenNamespace::NewL(ipNamespace->Prefix(), ipNamespace->URI());
	            CleanupStack::PushL(pCopy);
	#ifdef EKA2
	            aNamespaces.AppendL(pCopy);
	#else
	            User::LeaveIfError(aNamespaces.Append(pCopy));
	#endif
	            CleanupStack::Pop(pCopy);

	            // Change namespace object for this element to namespace object
	            // which can be found from aNamespaces array
	            ipNamespace = pCopy;
	            }
            }
        else
            {
            // Change namespace object for this element to namespace object
            // which can be found from aNamespaces array
            ipNamespace = aNamespaces[i];
            }
        }

    // Add namespaces from this element's attributes
    // if those namespaces are not already defined in
    // aNamespaces array.
    if ( ipAttrs && ipAttrs->Count() > 0 )
        {
        TInt colon = -1;
        const CSenNamespace* pNamespace = NULL;

        TInt count(ipAttrs->Count());
        for(TInt i=0; i<count; i++)
            {
            CSenBaseAttribute* pAttribute = (*ipAttrs)[i];

            // Check namespace assigned to Name
            colon = pAttribute->Name().Locate(':');
            if ( colon > 0 )
                {
                pNamespace =
                    Namespace(pAttribute->Name().Mid(0, colon), KNullDesC8);
                }
            if ( pNamespace && (aNamespaces.Find(pNamespace) == -1) )
                {
                // Check if namespace declaration for handled attribute
                // _name_ can be found from aNamespaces array.
                TInt count(aNamespaces.Count());
                TBool found(EFalse);
                for (TInt i = 0; i<count; i++)
                    {
                    if ( aNamespaces[i]->URI() == pNamespace->URI() && 
                         aNamespaces[i]->Prefix() == pNamespace->Prefix() )
                        {
                        found = ETrue;
                        break;
                        }
                    }
                if ( !found )
                    {
		            //If not found then check in namespace usage array that 
		            //namespace declaration is present or not. If yes then 
		            //continue without adding.
		            if((ipNamespaces && ipNamespaces->Find(pNamespace) == -1) || !ipNamespaces)
			            {
                    
	                    // Not found 
	                    // => Create a copy of namespace declaration
	                    //    and append it into aNamespaces array
	                    CSenNamespace* pCopy =
	                            CSenNamespace::NewL(pNamespace->Prefix(),
	                                                pNamespace->URI());
	                    CleanupStack::PushL(pCopy);
	#ifdef EKA2
	                    aNamespaces.AppendL(pCopy);
	#else
	                    User::LeaveIfError(aNamespaces.Append(pCopy));
	#endif
	                    CleanupStack::Pop(pCopy);
			            }
                    }
                }
            pNamespace = NULL;

            // Check namespace assigned to Value
            colon = pAttribute->Value().Locate(':');
            if ( colon > 0 )
                {
                pNamespace =
                    Namespace(pAttribute->Value().Mid(0, colon), KNullDesC8);
                }
            if ( pNamespace && (aNamespaces.Find(pNamespace) == -1) )
                {
                // Check if namespace declaration for handled attribute
                // _value_ can be found from aNamespaces array.
                TInt count(aNamespaces.Count());
                TBool found(EFalse);
                for (TInt i = 0; i<count; i++)
                    {
                    if ( aNamespaces[i]->URI() == pNamespace->URI() && 
                         aNamespaces[i]->Prefix() == pNamespace->Prefix() )
                        {
                        found = ETrue;
                        break;
                        }
                    }
                if ( !found )
                    {
					//If not found then check in namespace usage array that 
		            //namspace declaration is present or not.If yes then 
		            //continue without adding.
		            if(ipNamespaces && ipNamespaces->Find(pNamespace) == -1)
			            {

	                    // Not found 
	                    // => Create a copy of namespace declaration
	                    //    and append it into aNamespaces array
	                    CSenNamespace* pCopy =
	                            CSenNamespace::NewL(pNamespace->Prefix(), 
	                                                pNamespace->URI());
	                    CleanupStack::PushL(pCopy);
	#ifdef EKA2
	                    aNamespaces.AppendL(pCopy);
	#else
	                    User::LeaveIfError(aNamespaces.Append(pCopy));
	#endif
	                    CleanupStack::Pop(pCopy);
			            }
                    }
                }
            pNamespace = NULL;
            }
        }

    if(ipElements && ipElements->Count() > 0)
        {
        TInt count(ipElements->Count());
        for(TInt i=0; i<count; i++)
            {
            CSenXmlElement* pElement = (CSenXmlElement*) (*ipElements)[i];
            pElement->AddNamespaceMissingFromL(aNamespaces);
            }
        }
    }


EXPORT_C void CSenXmlElement::Set(const TDesC8& aNamespaceURI,
                                   const TDesC8& aLocalName,
                                   const TDesC8& aQName)
    {
    delete ipLocalName;
    ipLocalName = NULL;
    HBufC8* pTemp = aLocalName.Alloc();
    if(pTemp)
        {
        ipLocalName = pTemp;
        }

    TPtrC8 prefix(KNullDesC8);

    if(aQName != KNullDesC8)
        {
        TInt colon(KErrNotFound);
        colon = aQName.Locate(':');
        if(colon!=KErrNotFound)
            {
            prefix.Set(aQName.Left(colon));
            }
        }
    TInt leaveCode(KErrNone);
    TRAP(leaveCode, SetNamespaceL(prefix, aNamespaceURI);)
    leaveCode=0;
    
    //if (err) ; 
    }

EXPORT_C CSenElement* CSenXmlElement::Child(TInt aIndex)
    {
    CSenElement* pElement = NULL;
    if(ipElements && aIndex < ipElements->Count())
        {
        pElement = (CSenXmlElement*) (*ipElements)[aIndex];
        }
    return pElement;
    }

EXPORT_C TBool CSenXmlElement::ConsistsOfL(MSenElement& aCandidate)
    {
    // First check the names and namespaces
    if (    aCandidate.LocalName() != this->LocalName()
        ||  aCandidate.NamespaceURI() != this->NamespaceURI()
        ||  aCandidate.NsPrefix() != this->NsPrefix()
        )
        {
        return EFalse;
        }

    // Then check content if it exists
    if (aCandidate.HasContent())
        {
        if (aCandidate.Content() != this->Content())
            {
            return EFalse;  // Content doesn't match => no match
            }
        }

    // Then handle the children
    RPointerArray<CSenElement>& children = aCandidate.ElementsL();
    TInt childCount = children.Count();
    TInt i=0;
    while (i < childCount)
        {
        CSenElement* pMatchChild = children[i];
        CSenElement* pChild = this->Element(pMatchChild->LocalName());
        if (!pChild)
            {
            return EFalse;  // no child with same name found in children, 
                            // => no match
            }
        else
            {
            if (!pChild->ConsistsOfL(*pMatchChild))
                {
                return EFalse; // a non matching child was found => No match
                }
            }
        i++;
        }
    return ETrue;
    }

void CSenXmlElement::Compress()
    {
    if(ipContentWriteStream)
        {
        ipContentWriteStream->Close();
        delete ipContentWriteStream;
        ipContentWriteStream = NULL;
        }
    if (ipContentBuf) ipContentBuf->Compress();
    if(ipElements)
        {
        TInt count(ipElements->Count());

        for (TInt i = 0; i < count; i++)
            {
            CSenElement* pElement = (*ipElements)[i];
            ((CSenXmlElement*)pElement)->Compress();
            }
        }
    
    }

// New methods; problem: assume, that one cannot change CSenElement (abstract)
// class definition (virtual function tables); therefore one must be cautious
// and avoid class casting to internal element in base fragment layer(!) in
// order to access these methods! What if someone added a baseelement? -> crash

// Optimized variant
// @return NULL if this element has no child elements
/*
RPointerArray<CSenElement>* CSenXmlElement::ElementsL()
    {
    return ipElements;
    }

// Optimized variant
//@return NULL if this element has no attributes 
RPointerArray<CSenBaseAttribute>* CSenXmlElement::AttributesL()
    {
    return ipAttrs;
    }

// Optimized variant
// @return RPointerArray<CSenNamespace>* or NULL if there are no attributes in this element
RPointerArray<CSenNamespace>& CSenXmlElement::NamespacesL()
    {
    return ipNamespaces;
    }
*/

// end of file
