text:
/**
 *    @file
 *      This file implements parsers and encoders for messages in Weave
 *      Data Management (WDM) profile.
 *
 */

// __STDC_FORMAT_MACROS must be defined for PRIX64 to be defined for pre-C++11 clib
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif // __STDC_FORMAT_MACROS

// __STDC_LIMIT_MACROS must be defined for UINT8_MAX and INT32_MAX to be defined for pre-C++11 clib
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif // __STDC_LIMIT_MACROS

// __STDC_CONSTANT_MACROS must be defined for INT64_C and UINT64_C to be defined for pre-C++11 clib
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif // __STDC_CONSTANT_MACROS

#include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h>
#include <Weave/Profiles/data-management/DataManagement.h>

#include <Weave/Profiles/security/WeaveSecurity.h>

#include <Weave/Support/WeaveFaultInjection.h>

#include <stdio.h>

using namespace ::nl;
using namespace ::nl::Weave;
using namespace ::nl::Weave::TLV;

#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif

namespace nl {
namespace Weave {
namespace Profiles {
namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) {

#if WEAVE_DETAIL_LOGGING

static uint32_t gPrettyPrintingDepthLevel = 0;
static char gLineBuffer[256];
static uint32_t gCurLineBufferSize = 0;

/**
 * Simple object to checkpoint the pretty-print indentation level and make
 * sure it is restored at the end of a block (even in case of early exit).
 */
class PrettyPrintCheckpoint
{
public:
    PrettyPrintCheckpoint()
    {
        mLevel = gPrettyPrintingDepthLevel;
    }
    ~PrettyPrintCheckpoint()
    {
        gPrettyPrintingDepthLevel = mLevel;
    }
private:
    uint32_t mLevel;
};
#define PRETTY_PRINT_CHECKPOINT()  PrettyPrintCheckpoint lPrettyPrintCheckpoint;

#define PRETTY_PRINT(fmt, ...)                                                                                \
    do                                                                                \
    {                                                                                \
        PrettyPrintWDM(true, fmt, ##__VA_ARGS__);                                                                                \
    } while (0)
#define PRETTY_PRINT_SAMELINE(fmt, ...)                                                                                \
    do                                                                                \
    {                                                                                \
        PrettyPrintWDM(false, fmt, ##__VA_ARGS__);                                                                                \
    } while (0)
#define PRETTY_PRINT_INCDEPTH()                                                                                \
    do                                                                                \
    {                                                                                \
        gPrettyPrintingDepthLevel++;                                                                                \
    } while (0)
#define PRETTY_PRINT_DECDEPTH()                                                                                \
    do                                                                                \
    {                                                                                \
        gPrettyPrintingDepthLevel--;                                                                                \
    } while (0)

static void PrettyPrintWDM(bool aIsNewLine, const char * aFmt, ...)
{
    va_list args;
    int ret;
    int sizeLeft;

    va_start(args, aFmt);

    if (aIsNewLine)
    {
        if (gCurLineBufferSize)
        {
            // Don't need to explicitly NULL-terminate the string because
            // snprintf takes care of that.
            WeaveLogDetail(DataManagement, "%s", gLineBuffer);
            gCurLineBufferSize = 0;
        }

        for (uint32_t i = 0; i < gPrettyPrintingDepthLevel; i++)
        {
            if (sizeof(gLineBuffer) > gCurLineBufferSize)
            {
                sizeLeft = sizeof(gLineBuffer) - gCurLineBufferSize;
                ret      = snprintf(gLineBuffer + gCurLineBufferSize, sizeLeft, "\t");
                if (ret > 0)
                {
                    gCurLineBufferSize += MIN(ret, sizeLeft);
                }
            }
        }
    }

    if (sizeof(gLineBuffer) > gCurLineBufferSize)
    {
        sizeLeft = sizeof(gLineBuffer) - gCurLineBufferSize;
        ret      = vsnprintf(gLineBuffer + gCurLineBufferSize, sizeLeft, aFmt, args);
        if (ret > 0)
        {
            gCurLineBufferSize += MIN(ret, sizeLeft);
        }
    }

    va_end(args);
}
#else // WEAVE_DETAIL_LOGGING
#define PRETTY_PRINT_CHECKPOINT()
#define PRETTY_PRINT(fmt, ...)
#define PRETTY_PRINT_SAMELINE(fmt, ...)
#define PRETTY_PRINT_INCDEPTH()
#define PRETTY_PRINT_DECDEPTH()
#endif // WEAVE_DETAIL_LOGGING

WEAVE_ERROR LookForElementWithTag(const nl::Weave::TLV::TLVReader & aSrcReader, const uint64_t aTagInApiForm,
                                  nl::Weave::TLV::TLVReader * apDstReader)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    // make a copy of the Path reader
    nl::Weave::TLV::TLVReader reader;
    reader.Init(aSrcReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        // Documentation says the result of GetType must be verified before calling GetTag
        VerifyOrExit(nl::Weave::TLV::kTLVType_NotSpecified != reader.GetType(), err = WEAVE_ERROR_INVALID_TLV_ELEMENT);

        if (aTagInApiForm == reader.GetTag())
        {
            apDstReader->Init(reader);
            break;
        }
    }

exit:
    WeaveLogIfFalse((WEAVE_NO_ERROR == err) || (WEAVE_END_OF_TLV == err));

    return err;
}

ParserBase::ParserBase() { }

WEAVE_ERROR ParserBase::GetReaderOnTag(const uint64_t aTagToFind, nl::Weave::TLV::TLVReader * const apReader) const
{
    return LookForElementWithTag(mReader, aTagToFind, apReader);
}

template <typename T>
WEAVE_ERROR ParserBase::GetUnsignedInteger(const uint8_t aContextTag, T * const apLValue) const
{
    return GetSimpleValue(aContextTag, nl::Weave::TLV::kTLVType_UnsignedInteger, apLValue);
}

template <typename T>
WEAVE_ERROR ParserBase::GetSimpleValue(const uint8_t aContextTag, const nl::Weave::TLV::TLVType aTLVType, T * const apLValue) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;

    *apLValue = 0;

    err = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(aContextTag), &reader);
    SuccessOrExit(err);

    VerifyOrExit(aTLVType == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

    err = reader.Get(*apLValue);
    SuccessOrExit(err);

exit:
    WeaveLogIfFalse((WEAVE_NO_ERROR == err) || (WEAVE_END_OF_TLV == err));

    return err;
}

ListParserBase::ListParserBase() { }

WEAVE_ERROR ListParserBase::Init(const nl::Weave::TLV::TLVReader & aReader)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    // make a copy of the reader here
    mReader.Init(aReader);

    VerifyOrExit(nl::Weave::TLV::kTLVType_Array == mReader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

    // This is just a dummy, as we're not going to exit this container ever
    nl::Weave::TLV::TLVType OuterContainerType;
    err = mReader.EnterContainer(OuterContainerType);

exit:
    WeaveLogFunctError(err);

    return err;
}

WEAVE_ERROR ListParserBase::InitIfPresent(const nl::Weave::TLV::TLVReader & aReader, const uint8_t aContextTagToFind)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;

    err = LookForElementWithTag(aReader, nl::Weave::TLV::ContextTag(aContextTagToFind), &reader);
    SuccessOrExit(err);

    err = Init(reader);
    SuccessOrExit(err);

exit:
    WeaveLogIfFalse((WEAVE_NO_ERROR == err) || (WEAVE_END_OF_TLV == err));

    return err;
}

WEAVE_ERROR ListParserBase::Next(void)
{
    WEAVE_ERROR err = mReader.Next();

    WeaveLogIfFalse((WEAVE_NO_ERROR == err) || (WEAVE_END_OF_TLV == err));

    return err;
}

void ListParserBase::GetReader(nl::Weave::TLV::TLVReader * const apReader)
{
    apReader->Init(mReader);
}

BuilderBase::BuilderBase() :
    mError(WEAVE_ERROR_INCORRECT_STATE), mpWriter(NULL), mOuterContainerType(nl::Weave::TLV::kTLVType_NotSpecified)
{ }

void BuilderBase::ResetError()
{
    return ResetError(WEAVE_NO_ERROR);
}

void BuilderBase::ResetError(WEAVE_ERROR aErr)
{
    mError              = aErr;
    mOuterContainerType = nl::Weave::TLV::kTLVType_NotSpecified;
}

void BuilderBase::EndOfContainer()
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->EndContainer(mOuterContainerType);
    SuccessOrExit(mError);

    // we've just closed properly
    // mark it so we do not panic when the build object destructor is called
    mOuterContainerType = nl::Weave::TLV::kTLVType_NotSpecified;

exit:;
}

WEAVE_ERROR BuilderBase::InitAnonymousStructure(nl::Weave::TLV::TLVWriter * const apWriter)
{
    mpWriter            = apWriter;
    mOuterContainerType = nl::Weave::TLV::kTLVType_NotSpecified;
    mError = mpWriter->StartContainer(nl::Weave::TLV::AnonymousTag, nl::Weave::TLV::kTLVType_Structure, mOuterContainerType);
    WeaveLogFunctError(mError);

    return mError;
}

ListBuilderBase::ListBuilderBase(void) { }

WEAVE_ERROR ListBuilderBase::Init(nl::Weave::TLV::TLVWriter * const apWriter, const uint8_t aContextTagToUse)
{
    mpWriter            = apWriter;
    mOuterContainerType = nl::Weave::TLV::kTLVType_NotSpecified;
    mError =
        mpWriter->StartContainer(nl::Weave::TLV::ContextTag(aContextTagToUse), nl::Weave::TLV::kTLVType_Array, mOuterContainerType);
    WeaveLogFunctError(mError);

    return mError;
}

/**
 * Init the TLV array container with an anonymous tag.
 * Required to implement arrays of arrays, and to test ListBuilderBase.
 * There is no WDM message that has an array as the outermost container.
 *
 * @param[in]   apWriter    Pointer to the TLVWriter that is encoding the message.
 *
 * @return                  WEAVE_ERROR codes returned by Weave::TLV objects.
 */
WEAVE_ERROR ListBuilderBase::Init(nl::Weave::TLV::TLVWriter * const apWriter)
{
    mpWriter            = apWriter;
    mOuterContainerType = nl::Weave::TLV::kTLVType_NotSpecified;
    mError =
        mpWriter->StartContainer(nl::Weave::TLV::AnonymousTag, nl::Weave::TLV::kTLVType_Array, mOuterContainerType);
    WeaveLogFunctError(mError);

    return mError;
}

WEAVE_ERROR Path::Parser::Init(const nl::Weave::TLV::TLVReader & aReader)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    // make a copy of the reader here
    mReader.Init(aReader);

    VerifyOrExit(nl::Weave::TLV::kTLVType_Path == mReader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

    // This is just a dummy, as we're not going to exit this container ever
    nl::Weave::TLV::TLVType dummyContainerType;
    // enter into the Path
    err = mReader.EnterContainer(dummyContainerType);
    SuccessOrExit(err);
    err = mReader.Next();
    SuccessOrExit(err);

    VerifyOrExit(nl::Weave::TLV::ContextTag(kCsTag_InstanceLocator) == mReader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_ELEMENT);
    VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == mReader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

    // enter into the root section, we still need to call Next to access the first element
    err = mReader.EnterContainer(dummyContainerType);
    SuccessOrExit(err);

exit:
    WeaveLogFunctError(err);

    return err;
}

// Get a TLVReader at the additional tags section. Next() must be called before accessing it.
WEAVE_ERROR Path::Parser::GetTags(nl::Weave::TLV::TLVReader * const apReader) const
{
    nl::Weave::TLV::TLVType pathContainerType = nl::Weave::TLV::kTLVType_Path;
    apReader->Init(mReader);
    apReader->ExitContainer(pathContainerType);

    return WEAVE_NO_ERROR;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right, including
// 1) all mandatory tags are present
// 2) no unknown tags
// 3) all elements have expected data type
// 4) any tag can only appear once
WEAVE_ERROR Path::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err          = WEAVE_NO_ERROR;
    uint16_t TagPresenceMask = 0;
    nl::Weave::TLV::TLVReader reader;

    PRETTY_PRINT_SAMELINE("<Resource = {");

    // make a copy of the Path reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        VerifyOrExit(nl::Weave::TLV::IsContextTag(reader.GetTag()), err = WEAVE_ERROR_INVALID_TLV_TAG);
        switch (nl::Weave::TLV::TagNumFromTag(reader.GetTag()))
        {
        case kCsTag_ResourceID:
            // check if this tag has appeared before
            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_ResourceID)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_ResourceID);
            // Resource ID can be of any type, so no checking is done here

#if WEAVE_DETAIL_LOGGING
            {
                ResourceIdentifier resourceId;
                char strResource[ResourceIdentifier::MAX_STRING_LENGTH];
                err = resourceId.FromTLV(reader);
                if (err == WEAVE_NO_ERROR)
                {
                    resourceId.ToString(strResource, sizeof(strResource));
                    PRETTY_PRINT_SAMELINE("ResourceId = %s,", strResource);
                }
                else
                {
                    PRETTY_PRINT_SAMELINE("ResourceId = ??,");
                }
            }

#endif // WEAVE_DETAIL_LOGGING
            break;
        case kCsTag_TraitInstanceID:
            // check if this tag has appeared before
            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_TraitInstanceID)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_TraitInstanceID);
            // Instance ID can be of any type, so no checking is done here

#if WEAVE_DETAIL_LOGGING
            if (nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType())
            {
                uint64_t InstanceID;
                reader.Get(InstanceID);
                PRETTY_PRINT_SAMELINE("InstanceId = 0x%" PRIx64 ",", InstanceID);
            }
            else
            {
                PRETTY_PRINT_SAMELINE("InstanceId = ??");
            }
#endif // WEAVE_DETAIL_LOGGING
            break;

        case kCsTag_TraitProfileID:
        {
            SchemaVersionRange requestedVersion;
            uint32_t ProfileID;

            // check if this tag has appeared before
            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_TraitProfileID)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_TraitProfileID);

            if (reader.GetType() == nl::Weave::TLV::kTLVType_Array)
            {
                TLV::TLVType type;

                err = reader.EnterContainer(type);
                SuccessOrExit(err);

                err = reader.Next();
                SuccessOrExit(err);

                VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

                err = reader.Get(ProfileID);
                SuccessOrExit(err);

                err = reader.Next();
                VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_END_OF_TLV, );

                if (err == WEAVE_NO_ERROR)
                {
                    VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);
                    err = reader.Get(requestedVersion.mMaxVersion);
                    SuccessOrExit(err);
                }

                err = reader.Next();
                VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_END_OF_TLV, );

                if (err == WEAVE_NO_ERROR)
                {
                    VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);
                    err = reader.Get(requestedVersion.mMinVersion);
                    SuccessOrExit(err);
                }

                TagPresenceMask |= (1 << kCsTag_RequestedVersion);

                err = reader.Next();
                VerifyOrExit(err == WEAVE_END_OF_TLV, err = WEAVE_ERROR_WDM_MALFORMED_DATA_ELEMENT);

                err = reader.ExitContainer(type);
            }
            else
            {
                VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);
                err = reader.Get(ProfileID);
                SuccessOrExit(err);
            }

#if WEAVE_DETAIL_LOGGING
            {
                if (TagPresenceMask & (1 << kCsTag_RequestedVersion))
                {
                    PRETTY_PRINT_SAMELINE("[ProfileId = 0x%" PRIx32, ProfileID);

                    if (requestedVersion.mMaxVersion > 1)
                    {
                        PRETTY_PRINT_SAMELINE(", MaxVersion = %" PRIu32, requestedVersion.mMaxVersion);
                    }

                    if (requestedVersion.mMaxVersion > 1)
                    {
                        PRETTY_PRINT_SAMELINE(", MinVersion = %" PRIu32 "],", requestedVersion.mMinVersion);
                    }
                    else
                    {
                        PRETTY_PRINT_SAMELINE("],");
                    }
                }
                else
                {
                    PRETTY_PRINT_SAMELINE("ProfileId = 0x%" PRIx32 ",", ProfileID);
                }
            }
#endif // WEAVE_DETAIL_LOGGING

            break;
        }

        default:
            ExitNow(err = WEAVE_ERROR_INVALID_TLV_TAG);
        }
    }

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // okay if we have at least the Profile ID field
        if (TagPresenceMask & (1 << kCsTag_TraitProfileID))
        {
            err = WEAVE_NO_ERROR;
        }
    }
    SuccessOrExit(err);

    PRETTY_PRINT_SAMELINE("}");

    err = GetTags(&reader);
    SuccessOrExit(err);

    // Verify that the remaining additional tag section has only TAG=NULL elements, and the tags cannot be anonymous
    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        // Cannot have an anonymous element in this section
        VerifyOrExit(nl::Weave::TLV::AnonymousTag != reader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);
        // Can only have NULL value in this section
        VerifyOrExit(nl::Weave::TLV::kTLVType_Null == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
        const uint64_t tag = reader.GetTag();
        if (nl::Weave::TLV::IsContextTag(tag))
        {
            PRETTY_PRINT_SAMELINE("/0x%" PRIx32 " = null", nl::Weave::TLV::TagNumFromTag(tag));
        }
        else if (nl::Weave::TLV::IsProfileTag(tag))
        {
            PRETTY_PRINT_SAMELINE("/0x%" PRIx32 "::0x%" PRIx32 " = null", nl::Weave::TLV::ProfileIdFromTag(tag),
                                  nl::Weave::TLV::TagNumFromTag(tag));
        }
        else
        {
            PRETTY_PRINT_SAMELINE("?");
        }
#endif // WEAVE_DETAIL_LOGGING
    }

    PRETTY_PRINT_SAMELINE(">,");

    if (WEAVE_END_OF_TLV == err)
    {
        err = WEAVE_NO_ERROR;
    }
    SuccessOrExit(err);

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

// Resource ID could be of any type, so we can only position the reader so the caller has
// full information of tag, element type, length, and value
// WEAVE_END_OF_TLV if there is no such element
WEAVE_ERROR Path::Parser::GetResourceID(nl::Weave::TLV::TLVReader * const apReader) const
{
    WEAVE_ERROR err = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_ResourceID), apReader);

    WeaveLogIfFalse((WEAVE_NO_ERROR == err) || (WEAVE_END_OF_TLV == err));

    return err;
}

// Instance ID could be of any type, so we can only position the reader so the caller has
// full information of tag, element type, length, and value
WEAVE_ERROR Path::Parser::GetInstanceID(nl::Weave::TLV::TLVReader * const apReader) const
{
    WEAVE_ERROR err = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_TraitInstanceID), apReader);

    WeaveLogIfFalse((WEAVE_NO_ERROR == err) || (WEAVE_END_OF_TLV == err));

    return err;
}

// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types
WEAVE_ERROR Path::Parser::GetInstanceID(uint64_t * const apInstanceID) const
{
    return GetUnsignedInteger(kCsTag_TraitInstanceID, apInstanceID);
}

// Profile ID can only be uint32_t and not any other type
// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types
WEAVE_ERROR Path::Parser::GetProfileID(uint32_t * const apProfileID, SchemaVersionRange * const apSchemaVersionRange)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;
    nl::Weave::TLV::TLVType outerContainerType;

    apSchemaVersionRange->mMinVersion = 1;
    apSchemaVersionRange->mMaxVersion = 1;

    err = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_TraitProfileID), &reader);
    SuccessOrExit(err);

    if (reader.GetType() == nl::Weave::TLV::kTLVType_Array)
    {
        err = reader.EnterContainer(outerContainerType);
        SuccessOrExit(err);

        err = reader.Next();
        SuccessOrExit(err);

        VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

        err = reader.Get(*apProfileID);
        SuccessOrExit(err);

        err = reader.Next();
        VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_END_OF_TLV, );

        if (err == WEAVE_NO_ERROR)
        {
            VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

            err = reader.Get(apSchemaVersionRange->mMaxVersion);
            SuccessOrExit(err);
        }

        err = reader.Next();
        VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_END_OF_TLV, );

        if (err == WEAVE_NO_ERROR)
        {
            VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

            err = reader.Get(apSchemaVersionRange->mMinVersion);
            SuccessOrExit(err);
        }

        err = reader.Next();
        VerifyOrExit(err == WEAVE_END_OF_TLV, err = WEAVE_ERROR_WDM_MALFORMED_DATA_ELEMENT);
    }
    else
    {
        VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

        err = reader.Get(*apProfileID);
    }

exit:
    if (err == WEAVE_END_OF_TLV)
    {
        err = WEAVE_NO_ERROR;
    }

    return err;
}

WEAVE_ERROR Path::Builder::_Init(nl::Weave::TLV::TLVWriter * const apWriter, const uint64_t aTagInApiForm)
{
    mpWriter            = apWriter;
    mOuterContainerType = nl::Weave::TLV::kTLVType_NotSpecified;
    mError              = mpWriter->StartContainer(aTagInApiForm, nl::Weave::TLV::kTLVType_Path, mOuterContainerType);
    SuccessOrExit(mError);

    // We don't care about storing the outer container's type here, for we know it's a Path
    nl::Weave::TLV::TLVType dummyContainerType;
    mError = mpWriter->StartContainer(nl::Weave::TLV::ContextTag(kCsTag_InstanceLocator), nl::Weave::TLV::kTLVType_Structure,
                                      dummyContainerType);
    SuccessOrExit(mError);

    mInTagSection = false;

exit:
    WeaveLogFunctError(mError);

    return mError;
}

WEAVE_ERROR Path::Builder::Init(nl::Weave::TLV::TLVWriter * const apWriter)
{
    return _Init(apWriter, nl::Weave::TLV::AnonymousTag);
}

WEAVE_ERROR Path::Builder::Init(nl::Weave::TLV::TLVWriter * const apWriter, const uint8_t aContextTagToUse)
{
    return _Init(apWriter, nl::Weave::TLV::ContextTag(aContextTagToUse));
}

Path::Builder & Path::Builder::ResourceID(const uint64_t aResourceID)
{
    // skip if error has already been set
    SuccessOrExit(mError);
    // we can only add this field when we're already in the tag section of a Path
    VerifyOrExit(!mInTagSection, mError = WEAVE_ERROR_INCORRECT_STATE);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_ResourceID), aResourceID);

exit:
    WeaveLogFunctError(mError);

    return *this;
}

Path::Builder & Path::Builder::ResourceID(const ResourceIdentifier& aResourceID)
{
    // skip if error has already been set
    SuccessOrExit(mError);
    // we can only add this field when we're already in the tag section of a Path
    VerifyOrExit(!mInTagSection, mError = WEAVE_ERROR_INCORRECT_STATE);
    mError = aResourceID.ToTLV(*mpWriter);
    SuccessOrExit(mError);

exit:
    WeaveLogFunctError(mError);

    return *this;
}

Path::Builder & Path::Builder::InstanceID(const uint64_t aInstanceID)
{
    // skip if error has already been set
    SuccessOrExit(mError);
    // we can only add this field when we're already in the tag section of a Path
    VerifyOrExit(!mInTagSection, mError = WEAVE_ERROR_INCORRECT_STATE);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_TraitInstanceID), aInstanceID);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Path::Builder & Path::Builder::ProfileID(const uint32_t aProfileID)
{
    SchemaVersionRange versionRange;
    return ProfileID(aProfileID, versionRange);
}

Path::Builder & Path::Builder::ProfileID(const uint32_t aProfileID, const SchemaVersionRange & aSchemaVersionRange)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    // we can only add this field when we're already in the tag section of a Path
    VerifyOrExit(!mInTagSection, mError = WEAVE_ERROR_INCORRECT_STATE);

    VerifyOrExit(aSchemaVersionRange.IsValid(), mError = WEAVE_ERROR_INVALID_ARGUMENT);

    if (aSchemaVersionRange.mMaxVersion != 1 || aSchemaVersionRange.mMinVersion != 1)
    {
        TLV::TLVType type2;

        mError = mpWriter->StartContainer(TLV::ContextTag(Path::kCsTag_TraitProfileID), TLV::kTLVType_Array, type2);
        SuccessOrExit(mError);

        mError = mpWriter->Put(TLV::AnonymousTag, aProfileID);
        SuccessOrExit(mError);

        if (aSchemaVersionRange.mMaxVersion != 1)
        {
            mError = mpWriter->Put(TLV::AnonymousTag, aSchemaVersionRange.mMaxVersion);
            SuccessOrExit(mError);
        }

        if (aSchemaVersionRange.mMinVersion != 1)
        {
            mError = mpWriter->Put(TLV::AnonymousTag, aSchemaVersionRange.mMinVersion);
            SuccessOrExit(mError);
        }

        mError = mpWriter->EndContainer(type2);
        SuccessOrExit(mError);
    }
    else
    {
        mError = mpWriter->Put(TLV::ContextTag(Path::kCsTag_TraitProfileID), aProfileID);
        SuccessOrExit(mError);
    }

exit:
    return *this;
}

Path::Builder & Path::Builder::TagSection(void)
{
    // skip if error has already been set
    SuccessOrExit(mError);
    // we can only add this field when we're already in the tag section of a Path
    VerifyOrExit(!mInTagSection, mError = WEAVE_ERROR_INCORRECT_STATE);

    mError = mpWriter->EndContainer(nl::Weave::TLV::kTLVType_Path);
    WeaveLogFunctError(mError);

    mInTagSection = true;

exit:

    return *this;
}

Path::Builder & Path::Builder::AdditionalTag(const uint64_t aTagInApiForm)
{
    // skip if error has already been set
    SuccessOrExit(mError);
    // we can only add additional tags if we're in the tag section of a Path
    VerifyOrExit(mInTagSection, mError = WEAVE_ERROR_INCORRECT_STATE);

    mError = mpWriter->PutNull(aTagInApiForm);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Path::Builder & Path::Builder::EndOfPath(void)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    if (!mInTagSection)
    {
        // leave the first level container only if TagSection() hasn't been called
        mError = mpWriter->EndContainer(nl::Weave::TLV::kTLVType_Path);
        SuccessOrExit(mError);
    }

    EndOfContainer();

exit:

    return *this;
}

// aReader has to be on the element of StatusElement
WEAVE_ERROR StatusElement::Parser::Init(const nl::Weave::TLV::TLVReader & aReader)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    // make a copy of the reader here
    mReader.Init(aReader);

    switch (mReader.GetType())
    {
        case nl::Weave::TLV::kTLVType_Structure:
            mDeprecatedFormat = true;
            break;
        case nl::Weave::TLV::kTLVType_Array:
            mDeprecatedFormat = false;
            break;
        default:
            ExitNow(err = WEAVE_ERROR_WRONG_TLV_TYPE);
            break;
    }

    // This is just a dummy, as we're not going to exit this container ever
    nl::Weave::TLV::TLVType OuterContainerType;
    err = mReader.EnterContainer(OuterContainerType);

exit:

    WeaveLogFunctError(err);
    return err;
}

/**
 * Read the ProfileID and the StatusCode from the StatusElement.
 *
 * @param[out]   apProfileID     Pointer to the storage for the ProfileID
 * @param[out]   apStatusCode    Pointer to the storage for the StatusCode
 *
 * @return       WEAVE_ERROR codes returned by Weave::TLV objects. WEAVE_END_OF_TLV if either
 *               element is missing. WEAVE_ERROR_WRONG_TLV_TYPE if the elements are of the wrong
 *               type.
 */
WEAVE_ERROR StatusElement::Parser::GetProfileIDAndStatusCode(uint32_t * const apProfileID,
        uint16_t *const apStatusCode) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    if (mDeprecatedFormat)
    {
        err = GetUnsignedInteger(kCsTag_ProfileID, apProfileID);
        SuccessOrExit(err);
        err = GetUnsignedInteger(kCsTag_Status, apStatusCode);
        SuccessOrExit(err);
    }
    else
    {
        nl::Weave::TLV::TLVReader lReader;
        lReader.Init(mReader);

        err = lReader.Next();
        SuccessOrExit(err);
        VerifyOrExit(lReader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

        err = lReader.Get(*apProfileID);
        SuccessOrExit(err);

        err = lReader.Next();
        SuccessOrExit(err);
        VerifyOrExit(lReader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

        err = lReader.Get(*apStatusCode);
        SuccessOrExit(err);
    }
exit:
    return err;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
/**
 * Roughly verify the schema is right, including
 * 1) all mandatory tags are present
 * 2) all elements have expected data type
 * 3) any tag can only appear once
 * At the top level of the structure, unknown tags are ignored for forward compatibility.
 *
 * @return  WEAVE_NO_ERROR in case of success; WEAVE_ERROR codes
 *          returned by Weave::TLV objects otherwise.
 */
WEAVE_ERROR StatusElement::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err;

    if (mDeprecatedFormat)
    {
        err = CheckSchemaValidityDeprecated();
    }
    else
    {
        err = CheckSchemaValidityCurrent();
    }

    return err;
}

/**
 * Check the StatusElement is a structure with two elements with the
 * required context tags.
 * This format was deprecated for a more compact array based one.
 *
 * @return  WEAVE_NO_ERROR in case of success; WEAVE_ERROR codes
 *          returned by Weave::TLV objects otherwise.
 */
WEAVE_ERROR StatusElement::Parser::CheckSchemaValidityDeprecated(void) const
{
    WEAVE_ERROR err          = WEAVE_NO_ERROR;
    uint16_t TagPresenceMask = 0;
    nl::Weave::TLV::TLVReader reader;
    uint32_t tagNum = 0;

    PRETTY_PRINT("\t{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        VerifyOrExit(nl::Weave::TLV::IsContextTag(reader.GetTag()), err = WEAVE_ERROR_INVALID_TLV_TAG);

        tagNum = nl::Weave::TLV::TagNumFromTag(reader.GetTag());

        switch (tagNum)
        {
        case kCsTag_ProfileID:
            // check if this tag has appeared before
            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_ProfileID)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_ProfileID);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint32_t profileID;
                err = reader.Get(profileID);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tProfileID = 0x%" PRIx32 ",", profileID);
            }
#endif // WEAVE_DETAIL_LOGGING
            break;
        case kCsTag_Status:
            // check if this tag has appeared before
            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_Status)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_Status);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint16_t status;
                err = reader.Get(status);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tStatus = 0x%" PRIx16 ",", status);
            }
#endif // WEAVE_DETAIL_LOGGING
            break;

        default:
            PRETTY_PRINT("\t\tUnknown tag num %" PRIu32, tagNum);
            break;
        }
    }

    PRETTY_PRINT("\t},");

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // check for required fields:
        const uint16_t RequiredFields = (1 << kCsTag_Status) | (1 << kCsTag_ProfileID);

        if ((TagPresenceMask & RequiredFields) == RequiredFields)
        {
            err = WEAVE_NO_ERROR;
        }
        else
        {
            err = WEAVE_ERROR_WDM_MALFORMED_STATUS_ELEMENT;
        }
    }

exit:
    WeaveLogFunctError(err);

    return err;
}

/**
 * Check the StatusElement is an array with at least two unsigned integers.
 * The first one is the ProfileID, the second one is the StatusCode.
 *
 * @return  WEAVE_NO_ERROR in case of success; WEAVE_ERROR codes
 *          returned by Weave::TLV objects otherwise.
 */
WEAVE_ERROR StatusElement::Parser::CheckSchemaValidityCurrent(void) const
{
    WEAVE_ERROR err          = WEAVE_NO_ERROR;
    uint16_t TagPresenceMask = 0;
    nl::Weave::TLV::TLVReader reader;

    PRETTY_PRINT("\t{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        // This is an array; all elements are anonymous.
        VerifyOrExit(nl::Weave::TLV::AnonymousTag == reader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);

        if (!(TagPresenceMask & (1 << kCsTag_ProfileID)))
        {
            // The first element has to be the ProfileID.
            TagPresenceMask |= (1 << kCsTag_ProfileID);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint32_t profileID;
                err = reader.Get(profileID);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tProfileID = 0x%" PRIx32 ",", profileID);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (!(TagPresenceMask & (1 << kCsTag_Status)))
        {
            // The second element has to be the StatusCode.
            TagPresenceMask |= (1 << kCsTag_Status);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint16_t status;
                err = reader.Get(status);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tStatus = 0x%" PRIx16 ",", status);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else
        {
            PRETTY_PRINT("\t\tExtra element in StatusElement");
        }
    }

    PRETTY_PRINT("\t},");

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // check for required fields:
        const uint16_t RequiredFields = (1 << kCsTag_Status) | (1 << kCsTag_ProfileID);

        if ((TagPresenceMask & RequiredFields) == RequiredFields)
        {
            err = WEAVE_NO_ERROR;
        }
        else
        {
            err = WEAVE_ERROR_WDM_MALFORMED_STATUS_ELEMENT;
        }
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

WEAVE_ERROR StatusElement::Builder::Init(nl::Weave::TLV::TLVWriter * const apWriter)
{
    mDeprecatedFormat = false;
    return ListBuilderBase::Init(apWriter);
}

WEAVE_ERROR StatusElement::Builder::InitDeprecated(nl::Weave::TLV::TLVWriter * const apWriter)
{
    mDeprecatedFormat = true;
    return InitAnonymousStructure(apWriter);
}

StatusElement::Builder & StatusElement::Builder::ProfileIDAndStatus(const uint32_t aProfileID, const uint16_t aStatusCode)
{
    uint64_t tag = nl::Weave::TLV::AnonymousTag;
    SuccessOrExit(mError);

    if (mDeprecatedFormat)
    {
       tag = nl::Weave::TLV::ContextTag(kCsTag_ProfileID);
    }
    mError = mpWriter->Put(tag, aProfileID);

    if (mDeprecatedFormat)
    {
        tag = nl::Weave::TLV::ContextTag(kCsTag_Status);
    }
    mError = mpWriter->Put(tag, aStatusCode);

exit:
    WeaveLogFunctError(mError);

    return *this;
}

StatusElement::Builder & StatusElement::Builder::EndOfStatusElement(void)
{
    EndOfContainer();

    return *this;
}

// aReader has to be on the element of DataElement
WEAVE_ERROR DataElement::Parser::Init(const nl::Weave::TLV::TLVReader & aReader)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    // make a copy of the reader here
    mReader.Init(aReader);

    VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == mReader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

    // This is just a dummy, as we're not going to exit this container ever
    nl::Weave::TLV::TLVType OuterContainerType;
    err = mReader.EnterContainer(OuterContainerType);

exit:
    WeaveLogFunctError(err);

    return err;
}

WEAVE_ERROR
DataElement::Parser::ParseData(nl::Weave::TLV::TLVReader & aReader, int aDepth) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    if (aDepth == 0)
    {
        PRETTY_PRINT("\t\tData = ");
    }
    else
    {
        if (nl::Weave::TLV::IsContextTag(aReader.GetTag()))
        {
            PRETTY_PRINT("\t\t0x%" PRIx32 " = ", nl::Weave::TLV::TagNumFromTag(aReader.GetTag()));
        }
        else if (nl::Weave::TLV::IsProfileTag(aReader.GetTag()))
        {
            PRETTY_PRINT("\t\t0x%" PRIx32 "::0x%" PRIx32 " = ", nl::Weave::TLV::ProfileIdFromTag(aReader.GetTag()),
                         nl::Weave::TLV::TagNumFromTag(aReader.GetTag()));
        }
        else
        {
            // Anonymous tag, don't print anything
        }
    }

    switch (aReader.GetType())
    {
    case nl::Weave::TLV::kTLVType_Structure:
        PRETTY_PRINT("\t\t{");
        break;

    case nl::Weave::TLV::kTLVType_Array:
        PRETTY_PRINT_SAMELINE("[");
        PRETTY_PRINT("\t\t\t");
        break;

    case nl::Weave::TLV::kTLVType_SignedInteger:
    {
        int64_t value_s64;

        err = aReader.Get(value_s64);
        SuccessOrExit(err);

        PRETTY_PRINT_SAMELINE("%" PRId64 ", ", value_s64);
        break;
    }

    case nl::Weave::TLV::kTLVType_UnsignedInteger:
    {
        uint64_t value_u64;

        err = aReader.Get(value_u64);
        SuccessOrExit(err);

        PRETTY_PRINT_SAMELINE("%" PRIu64 ", ", value_u64);
        break;
    }

    case nl::Weave::TLV::kTLVType_Boolean:
    {
        bool value_b;

        err = aReader.Get(value_b);
        SuccessOrExit(err);

        PRETTY_PRINT_SAMELINE("%s, ", value_b ? "true" : "false");
        break;
    }

    case nl::Weave::TLV::kTLVType_UTF8String:
    {
        char value_s[256];

        err = aReader.GetString(value_s, sizeof(value_s));
        VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_ERROR_BUFFER_TOO_SMALL, );

        if (err == WEAVE_ERROR_BUFFER_TOO_SMALL)
        {
            PRETTY_PRINT_SAMELINE("... (byte string too long) ...");
            err = WEAVE_NO_ERROR;
        }
        else
        {
            PRETTY_PRINT_SAMELINE("\"%s\", ", value_s);
        }
        break;
    }

    case nl::Weave::TLV::kTLVType_ByteString:
    {
        uint8_t value_b[256];
        uint32_t len, readerLen;

        readerLen = aReader.GetLength();

        err = aReader.GetBytes(value_b, sizeof(value_b));
        VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_ERROR_BUFFER_TOO_SMALL, );

        PRETTY_PRINT_SAMELINE("[");
        PRETTY_PRINT("\t\t\t");

        if (readerLen < sizeof(value_b))
        {
            len = readerLen;
        }
        else
        {
            len = sizeof(value_b);
        }

        if (err == WEAVE_ERROR_BUFFER_TOO_SMALL)
        {
            PRETTY_PRINT_SAMELINE("... (byte string too long) ...");
        }
        else
        {
            for (size_t i = 0; i < len; i++)
            {
                PRETTY_PRINT_SAMELINE("0x%" PRIx8 ", ", value_b[i]);
            }
        }

        err = WEAVE_NO_ERROR;
        PRETTY_PRINT("\t\t]");
        break;
    }

    case nl::Weave::TLV::kTLVType_Null:
        PRETTY_PRINT_SAMELINE("NULL");
        break;

    default:
        PRETTY_PRINT_SAMELINE("--");
        break;
    }

    if (aReader.GetType() == nl::Weave::TLV::kTLVType_Structure || aReader.GetType() == nl::Weave::TLV::kTLVType_Array)
    {
        const char terminating_char = (aReader.GetType() == nl::Weave::TLV::kTLVType_Structure) ? '}' : ']';
        nl::Weave::TLV::TLVType type;

        IgnoreUnusedVariable(terminating_char);

        err = aReader.EnterContainer(type);
        SuccessOrExit(err);

        while ((err = aReader.Next()) == WEAVE_NO_ERROR)
        {
            PRETTY_PRINT_INCDEPTH();

            err = ParseData(aReader, aDepth + 1);
            SuccessOrExit(err);

            PRETTY_PRINT_DECDEPTH();
        }

        PRETTY_PRINT("\t\t%c,", terminating_char);

        err = aReader.ExitContainer(type);
        SuccessOrExit(err);
    }

exit:
    WeaveLogFunctError(err);
    return err;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right, including
// 1) all mandatory tags are present
// 2) all elements have expected data type
// 3) any tag can only appear once
// At the top level of the structure, unknown tags are ignored for foward compatibility
WEAVE_ERROR DataElement::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err          = WEAVE_NO_ERROR;
    uint16_t TagPresenceMask = 0;
    nl::Weave::TLV::TLVReader reader;
    uint32_t tagNum = 0;

    PRETTY_PRINT("\t{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        VerifyOrExit(nl::Weave::TLV::IsContextTag(reader.GetTag()), err = WEAVE_ERROR_INVALID_TLV_TAG);

        tagNum = nl::Weave::TLV::TagNumFromTag(reader.GetTag());

        switch (tagNum)
        {
        case kCsTag_Path:
            // check if this tag has appeared before
            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_Path)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_Path);
            VerifyOrExit(nl::Weave::TLV::kTLVType_Path == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

            PRETTY_PRINT("\t\tDataElementPath = ");

            {
                Path::Parser path;
                err = path.Init(reader);
                SuccessOrExit(err);
                err = path.CheckSchemaValidity();
                SuccessOrExit(err);
            }

            break;
        case kCsTag_Version:
            // check if this tag has appeared before
            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_Version)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_Version);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t version;
                err = reader.Get(version);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tDataElementVersion = 0x%" PRIx64 ",", version);
            }
#endif // WEAVE_DETAIL_LOGGING
            break;
        case kCsTag_Data:
            // check if this tag has appeared before
            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_Data)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_Data);

            err = ParseData(reader, 0);
            SuccessOrExit(err);
            break;

        case kCsTag_DeletedDictionaryKeys:
            nl::Weave::TLV::TLVType type;

            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_DeletedDictionaryKeys)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_DeletedDictionaryKeys);
            VerifyOrExit(nl::Weave::TLV::kTLVType_Array == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

            err = reader.EnterContainer(type);
            SuccessOrExit(err);

            PRETTY_PRINT("\t\tDataElement_DeletedDictionaryKeys =");
            PRETTY_PRINT("\t\t[");

            while ((err = reader.Next()) == WEAVE_NO_ERROR)
            {
                PropertyDictionaryKey key;

                err = reader.Get(key);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\t\t0x%" PRIx16 ",", key);
            }

            PRETTY_PRINT("\t\t],");

            err = reader.ExitContainer(type);
            SuccessOrExit(err);
            break;

        case kCsTag_IsPartialChange:
            // check if this tag has appeared before
            VerifyOrExit(!(TagPresenceMask & (1 << kCsTag_IsPartialChange)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kCsTag_IsPartialChange);
            VerifyOrExit(nl::Weave::TLV::kTLVType_Boolean == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                bool flag;
                err = reader.Get(flag);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tDataElement_IsPartialChange = %s,", flag ? "true" : "false");
            }

#endif // WEAVE_DETAIL_LOGGING
            break;

        default:
            PRETTY_PRINT("\t\tUnknown tag num %" PRIu32, tagNum);
            break;
        }
    }

    PRETTY_PRINT("\t},");

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // check for required fields:
        const uint16_t RequiredFields = (1 << kCsTag_Path);

        // Either the data or deleted keys should be present.
        const uint16_t DataElementTypeMask = (1 << kCsTag_Data) | (1 << kCsTag_DeletedDictionaryKeys);
        if ((TagPresenceMask & RequiredFields) == RequiredFields)
        {
            err = WEAVE_NO_ERROR;
        }
        else
        {
            err = WEAVE_ERROR_WDM_MALFORMED_DATA_ELEMENT;
        }
        if ((TagPresenceMask & DataElementTypeMask) == 0)
        {
            err = WEAVE_ERROR_WDM_MALFORMED_DATA_ELEMENT;
        }
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not a Path
WEAVE_ERROR DataElement::Parser::GetReaderOnPath(nl::Weave::TLV::TLVReader * const apReader) const
{
    WEAVE_ERROR err = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_Path), apReader);

    WeaveLogIfFalse((WEAVE_NO_ERROR == err) || (WEAVE_END_OF_TLV == err));

    return err;
}

// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not a Path
WEAVE_ERROR DataElement::Parser::GetPath(Path::Parser * const apPath) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;

    err = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_Path), &reader);
    SuccessOrExit(err);

    VerifyOrExit(nl::Weave::TLV::kTLVType_Path == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

    err = apPath->Init(reader);
    SuccessOrExit(err);

exit:
    WeaveLogIfFalse((WEAVE_NO_ERROR == err) || (WEAVE_END_OF_TLV == err));

    return err;
}

// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types
WEAVE_ERROR DataElement::Parser::GetVersion(uint64_t * const apVersion) const
{
    return GetUnsignedInteger(kCsTag_Version, apVersion);
}

// Data could be of any type, so we can only position the reader so the caller has
// full information of tag, element type, length, and value
WEAVE_ERROR DataElement::Parser::GetData(nl::Weave::TLV::TLVReader * const apReader) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    err = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_Data), apReader);
    SuccessOrExit(err);

exit:
    WeaveLogIfFalse((WEAVE_NO_ERROR == err) || (WEAVE_END_OF_TLV == err));

    return err;
}

// Default is false if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not a boolean
WEAVE_ERROR DataElement::Parser::GetPartialChangeFlag(bool * const apPartialChangeFlag) const
{
    return GetSimpleValue(kCsTag_IsPartialChange, nl::Weave::TLV::kTLVType_Boolean, apPartialChangeFlag);
}

WEAVE_ERROR DataElement::Parser::CheckPresence(bool * const apDataPresentFlag, bool * const apDeletePresentFlag) const
{
    nl::Weave::TLV::TLVReader reader;
    WEAVE_ERROR err_datamerge, err_dictionarydelete, err = WEAVE_NO_ERROR;

    err_datamerge        = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_Data), &reader);
    err_dictionarydelete = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_DeletedDictionaryKeys), &reader);

    if ((err_datamerge == WEAVE_END_OF_TLV) && (err_dictionarydelete == WEAVE_END_OF_TLV))
    {
        err = WEAVE_ERROR_WDM_MALFORMED_DATA_ELEMENT;
    }

    if (err_datamerge == WEAVE_NO_ERROR)
    {
        *apDataPresentFlag = true;
    }

    if (err_dictionarydelete == WEAVE_NO_ERROR)
    {
        *apDeletePresentFlag = true;
    }

    return err;
}

WEAVE_ERROR DataElement::Parser::GetDeletedDictionaryKeys(nl::Weave::TLV::TLVReader * const apReader) const
{
    WEAVE_ERROR err;
    nl::Weave::TLV::TLVType containerType;

    err = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_DeletedDictionaryKeys), apReader);
    SuccessOrExit(err);

    VerifyOrExit(apReader->GetType() == nl::Weave::TLV::kTLVType_Array, err = WEAVE_ERROR_WDM_MALFORMED_DATA_ELEMENT);

    err = apReader->EnterContainer(containerType);

exit:
    WeaveLogFunctError(err);
    return err;
}

// DataElement is only used in a Data List, which requires every path to be anonymous
// Note that both mWriter and mPathBuilder only hold reference to the actual TLVWriter
WEAVE_ERROR DataElement::Builder::Init(nl::Weave::TLV::TLVWriter * const apWriter)
{
    return InitAnonymousStructure(apWriter);
}

Path::Builder & DataElement::Builder::CreatePathBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mPathBuilder.ResetError(mError));

    mError = mPathBuilder.Init(mpWriter, nl::Weave::TLV::ContextTag(kCsTag_Path));
    WeaveLogFunctError(mError);

exit:

    // on error, mPathBuilder would be un-/partial initialized and cannot be used to write anything
    return mPathBuilder;
}

DataElement::Builder & DataElement::Builder::Version(const uint64_t aVersion)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_Version), aVersion);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

// Nothing would be written if aIsLastForCurrentChange == false, as that's the default value
DataElement::Builder & DataElement::Builder::PartialChange(const bool aIsPartialChange)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    if (aIsPartialChange)
    {
        mError = mpWriter->PutBoolean(nl::Weave::TLV::ContextTag(kCsTag_IsPartialChange), true);
        WeaveLogFunctError(mError);
    }

exit:
    return *this;
}

DataElement::Builder & DataElement::Builder::EndOfDataElement(void)
{
    EndOfContainer();

    return *this;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right, including
// 1) at least one element is there
//    (not checked since we want to subscribe to events only some times)
// 2) all elements are anonymous and of Path type
// 3) every path is also valid in schema
WEAVE_ERROR PathList::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    size_t NumPath  = 0;
    nl::Weave::TLV::TLVReader reader;

    PRETTY_PRINT("PathList =");
    PRETTY_PRINT("[");

    // make a copy of the PathList reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        VerifyOrExit(nl::Weave::TLV::AnonymousTag == reader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);
        VerifyOrExit(nl::Weave::TLV::kTLVType_Path == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

        {
            Path::Parser path;
            err = path.Init(reader);
            SuccessOrExit(err);

            PRETTY_PRINT("\t");

            err = path.CheckSchemaValidity();
            SuccessOrExit(err);
        }

        ++NumPath;
    }

    PRETTY_PRINT("],");

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        err = WEAVE_NO_ERROR;
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

// Re-initialize the shared PathBuilder with anonymous tag
Path::Builder & PathList::Builder::CreatePathBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mPathBuilder.ResetError(mError));

    mError = mPathBuilder.Init(mpWriter);
    WeaveLogFunctError(mError);

exit:

    // on error, mPathBuilder would be un-/partial initialized and cannot be used to write anything
    return mPathBuilder;
}

// Mark the end of this array and recover the type for outer container
PathList::Builder & PathList::Builder::EndOfPathList(void)
{
    EndOfContainer();

    return *this;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right, including
// 1) at least one element is there
// 2) all elements are anonymous and of Structure type
// 3) every Data Element is also valid in schema
WEAVE_ERROR DataList::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err       = WEAVE_NO_ERROR;
    size_t NumDataElement = 0;
    nl::Weave::TLV::TLVReader reader;

    PRETTY_PRINT("DataList =");
    PRETTY_PRINT("[");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        VerifyOrExit(nl::Weave::TLV::AnonymousTag == reader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);
        VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

        {
            DataElement::Parser data;
            err = data.Init(reader);
            SuccessOrExit(err);
            err = data.CheckSchemaValidity();
            SuccessOrExit(err);
        }

        ++NumDataElement;
    }

    PRETTY_PRINT("],");

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // if we have at least one data element
        if (NumDataElement > 0)
        {
            err = WEAVE_NO_ERROR;
        }
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

// Re-initialize the shared PathBuilder with anonymous tag
DataElement::Builder & DataList::Builder::CreateDataElementBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mDataElementBuilder.ResetError(mError));

    mError = mDataElementBuilder.Init(mpWriter);
    WeaveLogFunctError(mError);

exit:

    // on error, mPathBuilder would be un-/partial initialized and cannot be used to write anything
    return mDataElementBuilder;
}

// Mark the end of this array and recover the type for outer container
DataList::Builder & DataList::Builder::EndOfDataList(void)
{
    EndOfContainer();

    return *this;
}

WEAVE_ERROR Event::Parser::Init(const nl::Weave::TLV::TLVReader & aReader)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    // make a copy of the reader here
    mReader.Init(aReader);

    VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == mReader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

    // This is just a dummy, as we're not going to exit this container ever
    nl::Weave::TLV::TLVType OuterContainerType;
    err = mReader.EnterContainer(OuterContainerType);

exit:
    WeaveLogFunctError(err);

    return err;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right
WEAVE_ERROR Event::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;
    uint32_t tagNum = 0;

    struct TagPresence
    {
        bool Source : 1;
        bool Importance : 1;
        bool Id : 1;

        bool RelatedImportance : 1;
        bool RelatedId : 1;
        bool UTCTimestamp : 1;
        bool SystemTimestamp : 1;
        bool ResourceId : 1;
        bool TraitProfileId : 1;
        bool TraitInstanceId : 1;
        bool Type : 1;

        bool DeltaUTCTime : 1;
        bool DeltaSystemTime : 1;

        bool Data : 1;
    };

    TagPresence tagPresence = { 0 };

    PRETTY_PRINT("\t{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        VerifyOrExit(nl::Weave::TLV::IsContextTag(reader.GetTag()), err = WEAVE_ERROR_INVALID_TLV_TAG);

        tagNum = nl::Weave::TLV::TagNumFromTag(reader.GetTag());

        switch (tagNum)
        {
        case kCsTag_Source:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.Source == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.Source = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tSource = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_Importance:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.Importance == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.Importance = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tImportance = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_Id:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.Id == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.Id = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tId = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_RelatedImportance:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.RelatedImportance == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.RelatedImportance = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tRelatedImportance = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_RelatedId:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.RelatedId == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.RelatedId = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tRelatedId = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_UTCTimestamp:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.UTCTimestamp == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.UTCTimestamp = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tUTCTimestamp = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_SystemTimestamp:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.SystemTimestamp == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.SystemTimestamp = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tSystemTimestamp = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_ResourceId:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.ResourceId == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.ResourceId = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType() || nl::Weave::TLV::kTLVType_ByteString == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                ResourceIdentifier resourceId;
                char strResource[ResourceIdentifier::MAX_STRING_LENGTH];
                err = resourceId.FromTLV(reader);
                if (err == WEAVE_NO_ERROR)
                {
                    resourceId.ToString(strResource, sizeof(strResource));
                    PRETTY_PRINT("\t\t%s,", strResource);
                }
                else
                {
                    PRETTY_PRINT("\t\tResourceId = ??,");
                }
                SuccessOrExit(err);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_TraitProfileId:
        {
            SchemaVersionRange requestedVersion;
            uint32_t ProfileID;

            // check if this tag has appeared before
            VerifyOrExit(tagPresence.TraitProfileId == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.TraitProfileId = true;

            if (reader.GetType() == nl::Weave::TLV::kTLVType_Array)
            {
                TLV::TLVType type;

                err = reader.EnterContainer(type);
                SuccessOrExit(err);

                err = reader.Next();
                SuccessOrExit(err);

                VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

                err = reader.Get(ProfileID);
                SuccessOrExit(err);

                err = reader.Next();
                VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_END_OF_TLV, );

                if (err == WEAVE_NO_ERROR)
                {
                    VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

                    err = reader.Get(requestedVersion.mMaxVersion);
                    SuccessOrExit(err);
                }

                err = reader.Next();
                VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_END_OF_TLV, );

                if (err == WEAVE_NO_ERROR)
                {
                    VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

                    err = reader.Get(requestedVersion.mMinVersion);
                    SuccessOrExit(err);
                }

                err = reader.Next();
                VerifyOrExit(err == WEAVE_END_OF_TLV, err = WEAVE_ERROR_WDM_MALFORMED_DATA_ELEMENT);

                err = reader.ExitContainer(type);
            }
            else
            {
                VerifyOrExit(reader.GetType() == nl::Weave::TLV::kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);

                err = reader.Get(ProfileID);
                SuccessOrExit(err);
            }

#if WEAVE_DETAIL_LOGGING
            {
                if (requestedVersion.mMaxVersion > 1 || requestedVersion.mMinVersion > 1)
                {
                    PRETTY_PRINT("\t\tTraitProfileId = 0x%" PRIx32, ProfileID);

                    if (requestedVersion.mMaxVersion > 1)
                    {
                        PRETTY_PRINT_SAMELINE(", MaxVersion = %" PRIu32, requestedVersion.mMaxVersion);
                    }

                    if (requestedVersion.mMaxVersion > 1)
                    {
                        PRETTY_PRINT_SAMELINE(", MinVersion = %" PRIu32 ",", requestedVersion.mMinVersion);
                    }
                    else
                    {
                        PRETTY_PRINT_SAMELINE(",");
                    }
                }
                else
                {
                    PRETTY_PRINT("\t\tTraitProfileId = 0x%" PRIx32 ",", ProfileID);
                }
            }
#endif // WEAVE_DETAIL_LOGGING

            break;
        }

        case kCsTag_TraitInstanceId:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.TraitInstanceId == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.TraitInstanceId = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tTraitInstanceId = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_Type:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.Type == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.Type = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tType = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_DeltaUTCTime:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.DeltaUTCTime == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.DeltaUTCTime = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_SignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tDeltaUTCTime = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_DeltaSystemTime:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.DeltaSystemTime == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.DeltaSystemTime = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_SignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\t\tDeltaSystemTime = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_Data:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.Data == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.Data = true;

            err = ParseData(reader, 0);
            SuccessOrExit(err);
            break;

        default:
            PRETTY_PRINT("\t\tUnknown tag num %" PRIu32, tagNum);
            break;
        }
    }

    PRETTY_PRINT("\t},");
    PRETTY_PRINT("");

    // almost all fields in an Event are optional
    if (WEAVE_END_OF_TLV == err)
    {
        err = WEAVE_NO_ERROR;
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

WEAVE_ERROR Event::Parser::GetSourceId(uint64_t * const apSourceId)
{
    return GetUnsignedInteger(kCsTag_Source, apSourceId);
}

WEAVE_ERROR Event::Parser::GetImportance(uint64_t * const apImportance)
{
    return GetUnsignedInteger(kCsTag_Importance, apImportance);
}

WEAVE_ERROR Event::Parser::GetEventId(uint64_t * const apEventId)
{
    return GetUnsignedInteger(kCsTag_Id, apEventId);
}

WEAVE_ERROR Event::Parser::GetRelatedEventImportance(uint64_t * const apImportance)
{
    return GetUnsignedInteger(kCsTag_RelatedImportance, apImportance);
}

WEAVE_ERROR Event::Parser::GetRelatedEventId(uint64_t * const apEventId)
{
    return GetUnsignedInteger(kCsTag_RelatedId, apEventId);
}

WEAVE_ERROR Event::Parser::GetUTCTimestamp(uint64_t * const apUTCTimestamp)
{
    return GetUnsignedInteger(kCsTag_UTCTimestamp, apUTCTimestamp);
}

WEAVE_ERROR Event::Parser::GetSystemTimestamp(uint64_t * const apSystemTimestamp)
{
    return GetUnsignedInteger(kCsTag_SystemTimestamp, apSystemTimestamp);
}

WEAVE_ERROR Event::Parser::GetResourceId(uint64_t * const apResourceId)
{
    return GetUnsignedInteger(kCsTag_ResourceId, apResourceId);
}

WEAVE_ERROR Event::Parser::GetTraitProfileId(uint32_t * const apTraitProfileId)
{
    return GetUnsignedInteger(kCsTag_TraitProfileId, apTraitProfileId);
}

WEAVE_ERROR Event::Parser::GetTraitInstanceId(uint64_t * const apTraitInstanceId)
{
    return GetUnsignedInteger(kCsTag_TraitInstanceId, apTraitInstanceId);
}

WEAVE_ERROR Event::Parser::GetEventType(uint64_t * const apEventType)
{
    return GetUnsignedInteger(kCsTag_Type, apEventType);
}

WEAVE_ERROR Event::Parser::GetDeltaUTCTime(int64_t * const apDeltaUTCTime)
{
    return GetSimpleValue(kCsTag_DeltaUTCTime, nl::Weave::TLV::kTLVType_SignedInteger, apDeltaUTCTime);
}

WEAVE_ERROR Event::Parser::GetDeltaSystemTime(int64_t * const apDeltaSystemTime)
{
    return GetSimpleValue(kCsTag_DeltaSystemTime, nl::Weave::TLV::kTLVType_SignedInteger, apDeltaSystemTime);
}

WEAVE_ERROR Event::Parser::GetReaderOnEvent(nl::Weave::TLV::TLVReader * const apReader) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    err = LookForElementWithTag(mReader, nl::Weave::TLV::ContextTag(kCsTag_Data), apReader);
    WeaveLogFunctError(err);

    return err;
}

WEAVE_ERROR Event::Builder::Init(nl::Weave::TLV::TLVWriter * const apWriter)
{
    return InitAnonymousStructure(apWriter);
}

Event::Builder Event::Builder::SourceId(const uint64_t aSourceId)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_Source), aSourceId);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::Importance(const uint64_t aImportance)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_Importance), aImportance);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::EventId(const uint64_t aEventId)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_Id), aEventId);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::RelatedEventImportance(const uint64_t aImportance)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_RelatedImportance), aImportance);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::RelatedEventId(const uint64_t aEventId)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_RelatedId), aEventId);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::UTCTimestamp(const uint64_t aUTCTimestamp)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_UTCTimestamp), aUTCTimestamp);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::SystemTimestamp(const uint64_t aSystemTimestamp)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_SystemTimestamp), aSystemTimestamp);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::ResourceId(const uint64_t aResourceId)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_ResourceId), aResourceId);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::TraitProfileId(const uint32_t aTraitProfileId)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_TraitProfileId), aTraitProfileId);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::TraitInstanceId(const uint64_t aTraitInstanceId)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_TraitInstanceId), aTraitInstanceId);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::EventType(const uint64_t aEventType)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_Type), aEventType);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::DeltaUTCTime(const int64_t aDeltaUTCTime)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_DeltaUTCTime), aDeltaUTCTime);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

Event::Builder Event::Builder::DeltaSystemTime(const int64_t aDeltaSystemTime)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_DeltaSystemTime), aDeltaSystemTime);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

// Mark the end of this element and recover the type for outer container
Event::Builder & Event::Builder::EndOfEvent(void)
{
    EndOfContainer();

    return *this;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
WEAVE_ERROR EventList::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err       = WEAVE_NO_ERROR;
    size_t NumDataElement = 0;
    nl::Weave::TLV::TLVReader reader;

    PRETTY_PRINT("EventList =");
    PRETTY_PRINT("[");

    // make a copy of the EventList reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        VerifyOrExit(nl::Weave::TLV::AnonymousTag == reader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);
        VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

        {
            Event::Parser event;
            err = event.Init(reader);
            SuccessOrExit(err);
            err = event.CheckSchemaValidity();
            SuccessOrExit(err);
        }

        ++NumDataElement;
    }

    PRETTY_PRINT("],");

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // if we have at least one data element
        if (NumDataElement > 0)
        {
            err = WEAVE_NO_ERROR;
        }
        // NOTE: temporarily disable this check, to allow test to continue
        else
        {
            WeaveLogError(DataManagement, "PROTOCOL ERROR: Empty event list");
            err = WEAVE_NO_ERROR;
        }
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

Event::Builder & EventList::Builder::CreateEventBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mEventBuilder.ResetError(mError));

    mError = mEventBuilder.Init(mpWriter);
    WeaveLogFunctError(mError);

exit:

    // on error, mEventBuilder would be un-/partial initialized and cannot be used to write anything
    return mEventBuilder;
}

// Mark the end of this array and recover the type for outer container
EventList::Builder & EventList::Builder::EndOfEventList(void)
{
    EndOfContainer();

    return *this;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
WEAVE_ERROR VersionList::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;
    uint64_t version;
    size_t index = 0;

    PRETTY_PRINT("VersionList = ");
    PRETTY_PRINT("[");

    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        VerifyOrExit(nl::Weave::TLV::AnonymousTag == reader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);

        switch (reader.GetType())
        {
        case nl::Weave::TLV::kTLVType_Null:
            PRETTY_PRINT("\tNull,");
            break;

        case nl::Weave::TLV::kTLVType_UnsignedInteger:
            err = reader.Get(version);
            SuccessOrExit(err);

            PRETTY_PRINT("\t0x%" PRIx64 ",", version);
            break;

        default:
            ExitNow(err = WEAVE_ERROR_WRONG_TLV_TYPE);
            break;
        }

        ++index;
    }

    PRETTY_PRINT("],");

    if (WEAVE_END_OF_TLV == err)
    {
        err = WEAVE_NO_ERROR;
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

/**
 * Append a StatusElement to the list.
 *
 * @param[in] aProfileID    ProfileID
 * @param[in] aStatusCode   StatusCode
 *
 * @return          Reference to this builder.
 */
StatusList::Builder & StatusList::Builder::AddStatus(uint32_t aProfileID, uint16_t aStatusCode)
{
    StatusElement::Builder builder;

    SuccessOrExit(mError);

    if (mDeprecatedFormat)
    {
        builder.InitDeprecated(mpWriter);
    }
    else
    {
        builder.Init(mpWriter);
    }

    builder.ProfileIDAndStatus(aProfileID, aStatusCode);
    builder.EndOfStatusElement();

    mError = builder.GetError();

exit:
    WeaveLogFunctError(mError);

    return *this;
}

StatusList::Builder & StatusList::Builder::EndOfStatusList()
{
    EndOfContainer();

    return *this;
}

/**
 * Read the ProfileID and the StatusCode from the current StatusElement.
 *
 * @param[out]   apProfileID     Pointer to the storage for the ProfileID
 * @param[out]   apStatusCode    Pointer to the storage for the StatusCode
 *
 * @return       WEAVE_ERROR codes returned by Weave::TLV objects. WEAVE_END_OF_TLV if either
 *               element is missing. WEAVE_ERROR_WRONG_TLV_TYPE if the elements are of the wrong
 *               type.
 */
WEAVE_ERROR StatusList::Parser::GetProfileIDAndStatusCode(uint32_t * const apProfileID, uint16_t * const apStatusCode)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    StatusElement::Parser statusElement;

    err = statusElement.Init(mReader);
    SuccessOrExit(err);

    err = statusElement.GetProfileIDAndStatusCode(apProfileID, apStatusCode);
    SuccessOrExit(err);

exit:
    return err;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right, including
// 1) at least one element is there
// 2) all elements are anonymous and of Structure type
// 3) every Data Element is also valid in schema
WEAVE_ERROR StatusList::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err       = WEAVE_NO_ERROR;
    size_t NumStatusElement = 0;
    nl::Weave::TLV::TLVReader reader;

    PRETTY_PRINT("StatusList =");
    PRETTY_PRINT("[");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        TLVType type;
        uint64_t tag;

        // TODO: The spec says the StatusList should be an array of arrays, but in
        // the current implementation it's an array of structures. The array of
        // arrays is less intuitive but more space efficient.

        tag = reader.GetTag();
        VerifyOrExit(nl::Weave::TLV::AnonymousTag == tag, err = WEAVE_ERROR_INVALID_TLV_TAG);

        type = reader.GetType();
        VerifyOrExit((nl::Weave::TLV::kTLVType_Structure == type ||
                      nl::Weave::TLV::kTLVType_Array == type), err = WEAVE_ERROR_WRONG_TLV_TYPE);

        {
            StatusElement::Parser status;
            err = status.Init(reader);
            SuccessOrExit(err);
            err = status.CheckSchemaValidity();
            SuccessOrExit(err);
        }

        ++NumStatusElement;
    }

    PRETTY_PRINT("],");

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // The StatusList of an UpdateResponse can be empty, in case the update
        // was successful for all DataElements
        err = WEAVE_NO_ERROR;
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

// 1) current element is anonymous
// 2) current element is either unsigned integer or NULL
bool VersionList::Parser::IsElementValid(void)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    bool result     = false;

    VerifyOrExit(nl::Weave::TLV::AnonymousTag == mReader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);

    switch (mReader.GetType())
    {
    case nl::Weave::TLV::kTLVType_Null:
    case nl::Weave::TLV::kTLVType_UnsignedInteger:
        result = true;
        break;
    default:
        // err = WEAVE_ERROR_WRONG_TLV_TYPE;
        ExitNow();
        break;
    }

exit:
    WeaveLogFunctError(err);

    return result;
}

bool VersionList::Parser::IsNull(void)
{
    return (nl::Weave::TLV::kTLVType_Null == mReader.GetType());
}

WEAVE_ERROR VersionList::Parser::GetVersion(uint64_t * const apVersion)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    if (mReader.GetType() == kTLVType_Null)
    {
        *apVersion = 0;
        WeaveLogDetail(DataManagement, "Version is null in GetVersion");
    }
    else
    {
        err = mReader.Get(*apVersion);
    }
    return err;
}

VersionList::Builder & VersionList::Builder::AddVersion(const uint64_t aVersion)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::AnonymousTag, aVersion);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

VersionList::Builder & VersionList::Builder::AddNull(void)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->PutNull(nl::Weave::TLV::AnonymousTag);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

// Mark the end of this array and recover the type for outer container
VersionList::Builder & VersionList::Builder::EndOfVersionList(void)
{
    EndOfContainer();
    return *this;
}

// aReader has to be on the element of anonymous container
WEAVE_ERROR BaseMessageWithSubscribeId::Parser::Init(const nl::Weave::TLV::TLVReader & aReader)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    // make a copy of the reader here
    mReader.Init(aReader);

    VerifyOrExit(nl::Weave::TLV::AnonymousTag == mReader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);
    VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == mReader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

    // This is just a dummy, as we're not going to exit this container ever
    nl::Weave::TLV::TLVType OuterContainerType;
    err = mReader.EnterContainer(OuterContainerType);

    mReader.ImplicitProfileId = kWeaveProfile_DictionaryKey;

exit:
    WeaveLogFunctError(err);

    return err;
}

// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types
WEAVE_ERROR BaseMessageWithSubscribeId::Parser::GetSubscriptionID(uint64_t * const apSubscriptionID) const
{
    return GetUnsignedInteger(kCsTag_SubscriptionId, apSubscriptionID);
}

// Path is mostly used in a Path List, which requires every path to be anonymous
WEAVE_ERROR BaseMessageWithSubscribeId::Builder::Init(nl::Weave::TLV::TLVWriter * const apWriter)
{
    // error would be retained in mError and returned later
    (void) InitAnonymousStructure(apWriter);
    SuccessOrExit(mError);

    mpWriter->ImplicitProfileId = kWeaveProfile_DictionaryKey;

exit:
    return mError;
}

void BaseMessageWithSubscribeId::Builder::SetSubscriptionID(const uint64_t aSubscriptionID)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_SubscriptionId), aSubscriptionID);
    WeaveLogFunctError(mError);

exit:;
}

void BaseMessageWithSubscribeId::Builder::EndOfMessage(void)
{
    EndOfContainer();
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right, including
// 1) all mandatory tags are present
// 2) all elements have expected data type
// 3) any tag can only appear once
// At the top level of the message, unknown tags are ignored for foward compatibility
WEAVE_ERROR SubscribeRequest::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err          = WEAVE_NO_ERROR;
    uint16_t TagPresenceMask = 0;
    nl::Weave::TLV::TLVReader reader;

    enum
    {
        kBit_SubscriptionId          = 1,
        kBit_TimeoutMin              = 2,
        kBit_TimeoutMax              = 3,
        kBit_PathList                = 4,
        kBit_VersionList             = 5,
        kBit_SubscribeToAllEvents    = 6,
        kBit_LastObservedEventIdList = 7,
    };

    PRETTY_PRINT("{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        const uint64_t tag = reader.GetTag();
        if (nl::Weave::TLV::ContextTag(kCsTag_SubscriptionId) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_SubscriptionId)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_SubscriptionId);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t id;
                err = reader.Get(id);
                SuccessOrExit(err);

                PRETTY_PRINT("\tSubscriptionId = 0x%" PRIx64 ",", id);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_SubscribeTimeOutMin) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_TimeoutMin)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_TimeoutMin);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint32_t timeout;
                err = reader.Get(timeout);
                SuccessOrExit(err);

                PRETTY_PRINT("\tSubscriptionTimeoutMin = %u,", timeout);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_SubscribeTimeOutMax) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_TimeoutMax)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_TimeoutMax);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint32_t timeout;
                err = reader.Get(timeout);
                SuccessOrExit(err);

                PRETTY_PRINT("\tSubscriptionTimeoutMax = %u,", timeout);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_SubscribeToAllEvents) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_SubscribeToAllEvents)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_SubscribeToAllEvents);
            VerifyOrExit(nl::Weave::TLV::kTLVType_Boolean == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                bool SubscribeToAllEvents;
                err = reader.Get(SubscribeToAllEvents);
                SuccessOrExit(err);

                PRETTY_PRINT("\tSubscribeToAllEvents = %u,", SubscribeToAllEvents);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_LastObservedEventIdList) == tag)
        {
            EventList::Parser eventList;

            VerifyOrExit(!(TagPresenceMask & (1 << kBit_LastObservedEventIdList)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_LastObservedEventIdList);

            err = eventList.Init(reader);
            SuccessOrExit(err);

            PRETTY_PRINT_INCDEPTH();

            err = eventList.CheckSchemaValidity();
            SuccessOrExit(err);

            PRETTY_PRINT_DECDEPTH();
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_PathList) == tag)
        {
            PathList::Parser pathList;

            VerifyOrExit(!(TagPresenceMask & (1 << kBit_PathList)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_PathList);

            err = pathList.Init(reader);
            SuccessOrExit(err);

            PRETTY_PRINT_INCDEPTH();

            err = pathList.CheckSchemaValidity();
            SuccessOrExit(err);

            PRETTY_PRINT_DECDEPTH();
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_VersionList) == tag)
        {
            VersionList::Parser versionList;

            VerifyOrExit(!(TagPresenceMask & (1 << kBit_VersionList)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_VersionList);

            err = versionList.Init(reader);
            SuccessOrExit(err);

            PRETTY_PRINT_INCDEPTH();

            err = versionList.CheckSchemaValidity();
            SuccessOrExit(err);

            PRETTY_PRINT_DECDEPTH();
        }
        else
        {
            PRETTY_PRINT("\tUnknown tag 0x%" PRIx64, tag);
        }
    }

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // everything is optional
        err = WEAVE_NO_ERROR;
    }

    PRETTY_PRINT("}");
    PRETTY_PRINT("");

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not one of the right types
WEAVE_ERROR SubscribeRequest::Parser::GetSubscribeTimeoutMin(uint32_t * const apTimeOutMin) const
{
    return GetUnsignedInteger(kCsTag_SubscribeTimeOutMin, apTimeOutMin);
}

// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not one of the right types
WEAVE_ERROR SubscribeRequest::Parser::GetSubscribeTimeoutMax(uint32_t * const apTimeOutMax) const
{
    return GetUnsignedInteger(kCsTag_SubscribeTimeOutMax, apTimeOutMax);
}

WEAVE_ERROR SubscribeRequest::Parser::GetSubscribeToAllEvents(bool * const apAllEvents) const
{
    return GetSimpleValue(kCsTag_SubscribeToAllEvents, nl::Weave::TLV::kTLVType_Boolean, apAllEvents);
}

WEAVE_ERROR SubscribeRequest::Parser::GetLastObservedEventIdList(EventList::Parser * const apEventList) const
{
    return apEventList->InitIfPresent(mReader, kCsTag_LastObservedEventIdList);
}

// Get a TLVReader for the Paths. Next() must be called before accessing them.
WEAVE_ERROR SubscribeRequest::Parser::GetPathList(PathList::Parser * const apPathList) const
{
    return apPathList->InitIfPresent(mReader, kCsTag_PathList);
}

// Get a TLVReader at the Versions. Next() must be called before accessing it.
// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not one of the right types
WEAVE_ERROR SubscribeRequest::Parser::GetVersionList(VersionList::Parser * const apVersionList) const
{
    return apVersionList->InitIfPresent(mReader, kCsTag_VersionList);
}

SubscribeRequest::Builder & SubscribeRequest::Builder::SubscriptionID(const uint64_t aSubscriptionID)
{
    SetSubscriptionID(aSubscriptionID);
    return *this;
}

SubscribeRequest::Builder & SubscribeRequest::Builder::SubscribeTimeoutMin(const uint32_t aSubscribeTimeoutMin)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_SubscribeTimeOutMin), aSubscribeTimeoutMin);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

SubscribeRequest::Builder & SubscribeRequest::Builder::SubscribeTimeoutMax(const uint32_t aSubscribeTimeoutMax)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_SubscribeTimeOutMax), aSubscribeTimeoutMax);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

SubscribeRequest::Builder & SubscribeRequest::Builder::SubscribeToAllEvents(const bool aSubscribeToAllEvents)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->PutBoolean(nl::Weave::TLV::ContextTag(kCsTag_SubscribeToAllEvents), aSubscribeToAllEvents);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

EventList::Builder & SubscribeRequest::Builder::CreateLastObservedEventIdListBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mEventListBuilder.ResetError(mError));

    mError = mEventListBuilder.Init(mpWriter, kCsTag_LastObservedEventIdList);
    WeaveLogFunctError(mError);

exit:

    // on error, mPathListBuilder would be un-/partial initialized and cannot be used to write anything
    return mEventListBuilder;
}

PathList::Builder & SubscribeRequest::Builder::CreatePathListBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mPathListBuilder.ResetError(mError));

    mError = mPathListBuilder.Init(mpWriter, kCsTag_PathList);
    WeaveLogFunctError(mError);

exit:

    // on error, mPathListBuilder would be un-/partial initialized and cannot be used to write anything
    return mPathListBuilder;
}

VersionList::Builder & SubscribeRequest::Builder::CreateVersionListBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mVersionListBuilder.ResetError(mError));

    mError = mVersionListBuilder.Init(mpWriter, kCsTag_VersionList);
    WeaveLogFunctError(mError);

exit:

    // on error, mVersionListBuilder would be un-/partial initialized and cannot be used to write anything
    return mVersionListBuilder;
}

SubscribeRequest::Builder & SubscribeRequest::Builder::EndOfRequest(void)
{
    EndOfMessage();

    return *this;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right, including
// 1) all mandatory tags are present
// 2) all elements have expected data type
// 3) any tag can only appear once
// At the top level of the message, unknown tags are ignored for foward compatibility
WEAVE_ERROR SubscribeResponse::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err          = WEAVE_NO_ERROR;
    uint16_t TagPresenceMask = 0;
    nl::Weave::TLV::TLVReader reader;

    enum
    {
        kBit_SubscriptionId        = 1,
        kBit_Timeout               = 2,
        kBit_PossibleLossOfEvents  = 3,
        kBit_LastVendedEventIdList = 4,
    };

    PRETTY_PRINT("{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        const uint64_t tag = reader.GetTag();
        if (nl::Weave::TLV::ContextTag(kCsTag_SubscriptionId) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_SubscriptionId)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_SubscriptionId);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t id;
                err = reader.Get(id);
                SuccessOrExit(err);

                PRETTY_PRINT("\tSubscriptionId = 0x%" PRIx64 ",", id);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_SubscribeTimeOut) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_Timeout)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_Timeout);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint32_t timeout;
                err = reader.Get(timeout);
                SuccessOrExit(err);

                PRETTY_PRINT("\tSubscribeTimeOut = %u,", timeout);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_PossibleLossOfEvents) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_PossibleLossOfEvents)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_PossibleLossOfEvents);
            VerifyOrExit(nl::Weave::TLV::kTLVType_Boolean == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                bool PossibleLossOfEvents;
                err = reader.Get(PossibleLossOfEvents);
                SuccessOrExit(err);

                PRETTY_PRINT("\tPossibleLossOfEvents = %u,", PossibleLossOfEvents);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_LastVendedEventIdList) == tag)
        {
            // TODO: this is not an event list. It's a list of structures that have SourceID, EventImportance and EventID,
            // and only that.
            EventList::Parser eventList;

            VerifyOrExit(!(TagPresenceMask & (1 << kBit_LastVendedEventIdList)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_LastVendedEventIdList);

            err = eventList.Init(reader);
            SuccessOrExit(err);

            PRETTY_PRINT_INCDEPTH();

            err = eventList.CheckSchemaValidity();
            SuccessOrExit(err);

            PRETTY_PRINT_DECDEPTH();
        }
        else
        {
            PRETTY_PRINT("\tUnknown tag 0x%" PRIx64, tag);
        }
    }

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // if we have at least the PathList field
        if (TagPresenceMask & (1 << kBit_SubscriptionId))
        {
            err = WEAVE_NO_ERROR;
        }
    }

    PRETTY_PRINT("}");
    PRETTY_PRINT("");

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not one of the right types
WEAVE_ERROR SubscribeResponse::Parser::GetSubscribeTimeout(uint32_t * const apTimeOut) const
{
    return GetUnsignedInteger(kCsTag_SubscribeTimeOut, apTimeOut);
}

WEAVE_ERROR SubscribeResponse::Parser::GetPossibleLossOfEvents(bool * const apPossibleLossOfEvents) const
{
    return GetSimpleValue(kCsTag_PossibleLossOfEvents, nl::Weave::TLV::kTLVType_Boolean, apPossibleLossOfEvents);
}

WEAVE_ERROR SubscribeResponse::Parser::GetLastVendedEventIdList(EventList::Parser * const apEventList) const
{
    return apEventList->InitIfPresent(mReader, kCsTag_LastVendedEventIdList);
}

SubscribeResponse::Builder & SubscribeResponse::Builder::SubscriptionID(const uint64_t aSubscriptionID)
{
    SetSubscriptionID(aSubscriptionID);

    return *this;
}

SubscribeResponse::Builder & SubscribeResponse::Builder::SubscribeTimeout(const uint32_t aSubscribeTimeout)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_SubscribeTimeOut), aSubscribeTimeout);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

SubscribeResponse::Builder & SubscribeResponse::Builder::PossibleLossOfEvents(const bool aPossibleLossOfEvents)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->PutBoolean(nl::Weave::TLV::ContextTag(kCsTag_PossibleLossOfEvents), aPossibleLossOfEvents);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

EventList::Builder & SubscribeResponse::Builder::CreateLastVendedEventIdListBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mEventListBuilder.ResetError(mError));

    mError = mEventListBuilder.Init(mpWriter, kCsTag_LastVendedEventIdList);
    WeaveLogFunctError(mError);

exit:

    // on error, mPathListBuilder would be un-/partial initialized and cannot be used to write anything
    return mEventListBuilder;
}

SubscribeResponse::Builder & SubscribeResponse::Builder::EndOfResponse(void)
{
    EndOfMessage();

    return *this;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right, including
// 1) all mandatory tags are present
// 2) all elements have expected data type
// 3) any tag can only appear once
// At the top level of the message, unknown tags are ignored for foward compatibility
WEAVE_ERROR SubscribeCancelRequest::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err          = WEAVE_NO_ERROR;
    uint16_t TagPresenceMask = 0;
    nl::Weave::TLV::TLVReader reader;

    enum
    {
        kBit_SubscriptionId = 1,
    };

    PRETTY_PRINT("{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        const uint64_t tag = reader.GetTag();
        if (nl::Weave::TLV::ContextTag(kCsTag_SubscriptionId) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_SubscriptionId)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_SubscriptionId);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t id;
                err = reader.Get(id);
                SuccessOrExit(err);
                PRETTY_PRINT("\tSubscriptionId = 0x%" PRIx64 ",", id);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else
        {
            PRETTY_PRINT("\tUnknown tag 0x%" PRIx64, tag);
        }
    }

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // if we have at least the SubscriptionId field
        if (TagPresenceMask & (1 << kBit_SubscriptionId))
        {
            err = WEAVE_NO_ERROR;
        }
    }

    PRETTY_PRINT("}");
    PRETTY_PRINT("");

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

SubscribeCancelRequest::Builder & SubscribeCancelRequest::Builder::SubscriptionID(const uint64_t aSubscriptionID)
{
    SetSubscriptionID(aSubscriptionID);

    return *this;
}

SubscribeCancelRequest::Builder & SubscribeCancelRequest::Builder::EndOfRequest(void)
{
    EndOfMessage();

    return *this;
}

SubscribeConfirmRequest::Builder & SubscribeConfirmRequest::Builder::SubscriptionID(const uint64_t aSubscriptionID)
{
    SetSubscriptionID(aSubscriptionID);

    return *this;
}

SubscribeConfirmRequest::Builder & SubscribeConfirmRequest::Builder::EndOfRequest(void)
{
    EndOfMessage();

    return *this;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
// Roughly verify the schema is right, including
// 1) all mandatory tags are present
// 2) all elements have expected data type
// 3) any tag can only appear once
// At the top level of the message, unknown tags are ignored for foward compatibility
WEAVE_ERROR NotificationRequest::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err          = WEAVE_NO_ERROR;
    uint16_t TagPresenceMask = 0;
    nl::Weave::TLV::TLVReader reader;
    DataList::Parser dataList;
    EventList::Parser eventList;

    enum
    {
        kBit_SubscriptionId      = 1,
        kBit_DataList            = 2,
        kBit_PossibleLossOfEvent = 3,
        kBit_UTCTimestamp        = 4,
        kBit_SystemTimestamp     = 5,
        kBit_EventList           = 6,
    };

    PRETTY_PRINT("{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        const uint64_t tag = reader.GetTag();

        if (nl::Weave::TLV::ContextTag(kCsTag_SubscriptionId) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_SubscriptionId)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_SubscriptionId);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t id;
                err = reader.Get(id);
                SuccessOrExit(err);
                PRETTY_PRINT("\tSubscriptionId = 0x%" PRIx64 ",", id);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_PossibleLossOfEvent) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_PossibleLossOfEvent)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_PossibleLossOfEvent);
            VerifyOrExit(nl::Weave::TLV::kTLVType_Boolean == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                bool PossibleLossOfEvent;
                err = reader.Get(PossibleLossOfEvent);
                SuccessOrExit(err);
                PRETTY_PRINT("\tPossibleLossOfEvent = %u,", PossibleLossOfEvent);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_UTCTimestamp) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_UTCTimestamp)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_UTCTimestamp);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t UTCTimestamp;
                err = reader.Get(UTCTimestamp);
                SuccessOrExit(err);
                PRETTY_PRINT("\tUTCTimestamp = 0x%" PRIx64 ",", UTCTimestamp);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_SystemTimestamp) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_SystemTimestamp)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_SystemTimestamp);
            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t SystemTimestamp;
                err = reader.Get(SystemTimestamp);
                SuccessOrExit(err);
                PRETTY_PRINT("\tSystemTimestamp = 0x%" PRIx64 ",", SystemTimestamp);
            }
#endif // WEAVE_DETAIL_LOGGING
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_EventList) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_EventList)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_EventList);
            VerifyOrExit(nl::Weave::TLV::kTLVType_Array == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

            eventList.Init(reader);

            PRETTY_PRINT_INCDEPTH();

            err = eventList.CheckSchemaValidity();
            SuccessOrExit(err);

            PRETTY_PRINT_DECDEPTH();
        }
        else if (nl::Weave::TLV::ContextTag(kCsTag_DataList) == tag)
        {
            VerifyOrExit(!(TagPresenceMask & (1 << kBit_DataList)), err = WEAVE_ERROR_INVALID_TLV_TAG);
            TagPresenceMask |= (1 << kBit_DataList);
            VerifyOrExit(nl::Weave::TLV::kTLVType_Array == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

            dataList.Init(reader);

            PRETTY_PRINT_INCDEPTH();

            err = dataList.CheckSchemaValidity();
            SuccessOrExit(err);

            PRETTY_PRINT_DECDEPTH();
        }
        else
        {
            PRETTY_PRINT("\tUnknown tag 0x%" PRIx64, tag);
        }

    }

    PRETTY_PRINT("}");
    PRETTY_PRINT("");

    // if we have exhausted this container
    if (WEAVE_END_OF_TLV == err)
    {
        // if we have at least the DataList or EventList field
        if ((TagPresenceMask & (1 << kBit_DataList)) ||
            (TagPresenceMask & (1 << kBit_EventList)))
        {
            err = WEAVE_NO_ERROR;
        }
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

// Get a TLVReader for the Paths. Next() must be called before accessing them.
WEAVE_ERROR NotificationRequest::Parser::GetDataList(DataList::Parser * const apDataList) const
{
    return apDataList->InitIfPresent(mReader, kCsTag_DataList);
}

WEAVE_ERROR NotificationRequest::Parser::GetPossibleLossOfEvent(bool * const apPossibleLossOfEvent) const
{
    return GetSimpleValue(kCsTag_PossibleLossOfEvent, nl::Weave::TLV::kTLVType_Boolean, apPossibleLossOfEvent);
}

WEAVE_ERROR NotificationRequest::Parser::GetUTCTimestamp(uint64_t * const apUTCTimestamp) const
{
    return GetUnsignedInteger(kCsTag_UTCTimestamp, apUTCTimestamp);
}

WEAVE_ERROR NotificationRequest::Parser::GetSystemTimestamp(uint64_t * const apSystemTimestamp) const
{
    return GetUnsignedInteger(kCsTag_SystemTimestamp, apSystemTimestamp);
}

WEAVE_ERROR NotificationRequest::Parser::GetEventList(EventList::Parser * const apEventList) const
{
    return apEventList->InitIfPresent(mReader, kCsTag_EventList);
}

WEAVE_ERROR CustomCommand::Parser::Init(const nl::Weave::TLV::TLVReader & aReader)
{

    WEAVE_ERROR err = WEAVE_NO_ERROR;

    VerifyOrExit(nl::Weave::TLV::AnonymousTag == aReader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);

    err = DataElement::Parser::Init(aReader);
    SuccessOrExit(err);

    mReader.ImplicitProfileId = kWeaveProfile_DictionaryKey;

exit:
    WeaveLogFunctError(err);

    return err;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
WEAVE_ERROR CustomCommand::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;

    struct TagPresence
    {
        bool Path;
        bool CommandType;
        bool InitiationTime;
        bool ActionTime;
        bool ExpiryTime;
        bool MustBeVersion;
        bool Argument;
        bool Authenticator;
    };

    TagPresence tagPresence = { 0 };

    PRETTY_PRINT("{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        // Authenticators carry profile tags
        const uint64_t tag = reader.GetTag();

        if (nl::Weave::TLV::IsContextTag(tag))
        {
            switch (nl::Weave::TLV::TagNumFromTag(tag))
            {
            case CustomCommand::kCsTag_Path:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.Path == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.Path = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_Path == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

                PRETTY_PRINT("\tCommand Path = ");

                {
                    Path::Parser path;
                    err = path.Init(reader);
                    SuccessOrExit(err);
                    err = path.CheckSchemaValidity();
                    SuccessOrExit(err);
                }

                break;

            case kCsTag_CommandType:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.CommandType == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.CommandType = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    uint64_t value;
                    err = reader.Get(value);
                    SuccessOrExit(err);

                    PRETTY_PRINT("\tCommand Type = 0x%" PRIx64 ",", value);
                }
#endif // WEAVE_DETAIL_LOGGING

                break;

            case kCsTag_InitiationTime:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.InitiationTime == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.InitiationTime = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_SignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    int64_t value;
                    err = reader.Get(value);
                    SuccessOrExit(err);

                    PRETTY_PRINT("\tInit Time = 0x%" PRIx64 ",", static_cast<uint64_t>(value));
                }
#endif // WEAVE_DETAIL_LOGGING

                break;

            case kCsTag_ActionTime:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.ActionTime == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.ActionTime = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_SignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    int64_t value;
                    err = reader.Get(value);
                    SuccessOrExit(err);

                    PRETTY_PRINT("\tAction Time = 0x%" PRIx64 ",", static_cast<uint64_t>(value));
                }
#endif // WEAVE_DETAIL_LOGGING

                break;

            case kCsTag_ExpiryTime:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.ExpiryTime == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.ExpiryTime = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_SignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    int64_t value;
                    err = reader.Get(value);
                    SuccessOrExit(err);

                    PRETTY_PRINT("\tExpiry Time = 0x%" PRIx64 ",", static_cast<uint64_t>(value));
                }
#endif // WEAVE_DETAIL_LOGGING

                break;

            case kCsTag_MustBeVersion:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.MustBeVersion == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.MustBeVersion = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    uint64_t value;
                    err = reader.Get(value);
                    SuccessOrExit(err);

                    PRETTY_PRINT("\tMust Be Version = 0x%" PRIx64 ",", value);
                }
#endif // WEAVE_DETAIL_LOGGING

                break;

            case kCsTag_Argument:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.Argument == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.Argument = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    PRETTY_PRINT("\t(Argument)");

                    err = ParseData(reader, 0);
                    SuccessOrExit(err);
                }
#endif // WEAVE_DETAIL_LOGGING

                break;

            default:
                WeaveLogDetail(DataManagement, "UNKONWN, IGNORE");
            }
        }
        else if (nl::Weave::TLV::IsProfileTag(tag))
        {
            // we cannot use the normal case statement, for ProfileTag is a function instead of a macro
            if (tag ==
                nl::Weave::TLV::ProfileTag(nl::Weave::Profiles::kWeaveProfile_Security,
                                           nl::Weave::Profiles::Security::kTag_WeaveSignature))
            {
                // certificate signature

                VerifyOrExit(tagPresence.Authenticator == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.Authenticator = true;

#if WEAVE_DETAIL_LOGGING
                {
                    PRETTY_PRINT("\t(Authenticator-Certificate)");

                    err = ParseData(reader, 0);
                    SuccessOrExit(err);
                }
#endif // WEAVE_DETAIL_LOGGING
            }
            // we cannot use the normal case statement, for ProfileTag is a function instead of a macro
            // 10 is a place holder for the new group key feature in security profile
            else if (tag ==
                     nl::Weave::TLV::ProfileTag(nl::Weave::Profiles::kWeaveProfile_Security,
                                                nl::Weave::Profiles::Security::kTag_GroupKeySignature))
            {
                // group key signature

                VerifyOrExit(tagPresence.Authenticator == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.Authenticator = true;

#if WEAVE_DETAIL_LOGGING
                {
                    PRETTY_PRINT("\t(Authenticator-Group Key)");

                    err = ParseData(reader, 0);
                    SuccessOrExit(err);
                }
#endif // WEAVE_DETAIL_LOGGING
            }
        }
        else
        {
            // a custom command can only contain, at top level, context-specific tags or profile tags
            ExitNow(err = WEAVE_ERROR_INVALID_TLV_TAG);
        }
    }

    PRETTY_PRINT("}");
    PRETTY_PRINT("");

    // almost all fields in an Event are optional
    if (WEAVE_END_OF_TLV == err)
    {
        err = WEAVE_NO_ERROR;
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

WEAVE_ERROR CustomCommand::Parser::GetMustBeVersion(uint64_t * const apMustBeVersion) const
{
    return GetUnsignedInteger(kCsTag_MustBeVersion, apMustBeVersion);
}

WEAVE_ERROR CustomCommand::Parser::GetInitiationTimeMicroSecond(int64_t * const apInitiationTimeMicroSecond) const
{
    return GetSimpleValue(kCsTag_InitiationTime, nl::Weave::TLV::kTLVType_SignedInteger, apInitiationTimeMicroSecond);
}

WEAVE_ERROR CustomCommand::Parser::GetActionTimeMicroSecond(int64_t * const apActionTimeMicroSecond) const
{
    return GetSimpleValue(kCsTag_ActionTime, nl::Weave::TLV::kTLVType_SignedInteger, apActionTimeMicroSecond);
}

WEAVE_ERROR CustomCommand::Parser::GetExpiryTimeMicroSecond(int64_t * const apExpiryTimeMicroSecond) const
{
    return GetSimpleValue(kCsTag_ExpiryTime, nl::Weave::TLV::kTLVType_SignedInteger, apExpiryTimeMicroSecond);
}

WEAVE_ERROR CustomCommand::Parser::GetCommandType(uint64_t * const apCommandType) const
{
    return GetUnsignedInteger(kCsTag_CommandType, apCommandType);
}

WEAVE_ERROR CustomCommand::Parser::GetPath(Path::Parser * const apPath) const
{
    // CustomCommand::kCsTag_Path is defined to be 1, which happens to be
    // the same as DataElement::kCsTag_Path
    return DataElement::Parser::GetPath(apPath);
}

WEAVE_ERROR CustomCommand::Parser::GetReaderOnArgument(nl::Weave::TLV::TLVReader * const apReader) const
{
    return GetReaderOnTag(nl::Weave::TLV::ContextTag(kCsTag_Argument), apReader);
}

WEAVE_ERROR CustomCommand::Parser::GetReaderOnPath(nl::Weave::TLV::TLVReader * const apReader) const
{
    return GetReaderOnTag(nl::Weave::TLV::ContextTag(kCsTag_Path), apReader);
}

WEAVE_ERROR CustomCommand::Builder::Init(nl::Weave::TLV::TLVWriter * const apWriter)
{
    return InitAnonymousStructure(apWriter);
}

Path::Builder & CustomCommand::Builder::CreatePathBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mPathBuilder.ResetError(mError));

    mError = mPathBuilder.Init(mpWriter, nl::Weave::TLV::ContextTag(kCsTag_Path));
    WeaveLogFunctError(mError);

exit:

    // on error, mPathBuilder would be un-/partial initialized and cannot be used to write anything
    return mPathBuilder;
}

CustomCommand::Builder & CustomCommand::Builder::CommandType(const uint64_t aCommandType)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_CommandType), aCommandType);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

CustomCommand::Builder & CustomCommand::Builder::InitiationTimeMicroSecond(const int64_t aInitiationTimeMicroSecond)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_InitiationTime), aInitiationTimeMicroSecond);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

CustomCommand::Builder & CustomCommand::Builder::ActionTimeMicroSecond(const int64_t aActionTimeMicroSecond)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_ActionTime), aActionTimeMicroSecond);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

CustomCommand::Builder & CustomCommand::Builder::ExpiryTimeMicroSecond(const int64_t aExpiryTimeMicroSecond)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    WEAVE_FAULT_INJECT(FaultInjection::kFault_WDM_SendCommandExpired, {
        int64_t * tmp = const_cast<int64_t *>(&aExpiryTimeMicroSecond);
        *tmp          = 0;
    });

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_ExpiryTime), aExpiryTimeMicroSecond);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

CustomCommand::Builder & CustomCommand::Builder::MustBeVersion(const uint64_t aMustBeVersion)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    WEAVE_FAULT_INJECT(FaultInjection::kFault_WDM_SendCommandBadVersion, {
        uint64_t * tmp = const_cast<uint64_t *>(&aMustBeVersion);
        *tmp           = ~(*tmp);
    });

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_MustBeVersion), aMustBeVersion);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

CustomCommand::Builder & CustomCommand::Builder::EndOfCustomCommand(void)
{
    EndOfContainer();

    return *this;
}

WEAVE_ERROR CustomCommandResponse::Parser::Init(const nl::Weave::TLV::TLVReader & aReader)
{

    WEAVE_ERROR err = WEAVE_NO_ERROR;

    VerifyOrExit(nl::Weave::TLV::AnonymousTag == aReader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);

    err = DataElement::Parser::Init(aReader);
    SuccessOrExit(err);

    mReader.ImplicitProfileId = kWeaveProfile_DictionaryKey;

exit:
    WeaveLogFunctError(err);

    return err;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
WEAVE_ERROR CustomCommandResponse::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;

    struct TagPresence
    {
        bool Version;
        bool Response;
    };

    TagPresence tagPresence = { 0 };

    PRETTY_PRINT("{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        VerifyOrExit(nl::Weave::TLV::IsContextTag(reader.GetTag()), err = WEAVE_ERROR_INVALID_TLV_TAG);
        switch (nl::Weave::TLV::TagNumFromTag(reader.GetTag()))
        {
        case kCsTag_Version:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.Version == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.Version = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                uint64_t value;
                err = reader.Get(value);
                SuccessOrExit(err);

                PRETTY_PRINT("\tVersion = 0x%" PRIx64 ",", value);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        case kCsTag_Response:
            // check if this tag has appeared before
            VerifyOrExit(tagPresence.Response == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
            tagPresence.Response = true;

            VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
            {
                PRETTY_PRINT("\t(Response)");

                err = ParseData(reader, 0);
                SuccessOrExit(err);
            }
#endif // WEAVE_DETAIL_LOGGING

            break;

        default:
            WeaveLogDetail(DataManagement, "UNKONWN, IGNORE");
        }
    }

    PRETTY_PRINT("}");
    PRETTY_PRINT("");

    // almost all fields in an Event are optional
    if (WEAVE_END_OF_TLV == err)
    {
        err = WEAVE_NO_ERROR;
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

WEAVE_ERROR CustomCommandResponse::Parser::GetVersion(uint64_t * const apVersion) const
{
    return GetUnsignedInteger(kCsTag_Version, apVersion);
}

WEAVE_ERROR CustomCommandResponse::Parser::GetReaderOnResponse(nl::Weave::TLV::TLVReader * const apReader) const
{
    return GetReaderOnTag(nl::Weave::TLV::ContextTag(kCsTag_Response), apReader);
}

WEAVE_ERROR CustomCommandResponse::Builder::Init(nl::Weave::TLV::TLVWriter * const apWriter)
{
    return InitAnonymousStructure(apWriter);
}

CustomCommandResponse::Builder & CustomCommandResponse::Builder::Version(const uint64_t aMustBeVersion)
{
    // skip if error has already been set
    SuccessOrExit(mError);

    mError = mpWriter->Put(nl::Weave::TLV::ContextTag(kCsTag_Version), aMustBeVersion);
    WeaveLogFunctError(mError);

exit:

    return *this;
}

CustomCommandResponse::Builder & CustomCommandResponse::Builder::EndOfResponse(void)
{
    EndOfContainer();

    return *this;
}

WEAVE_ERROR UpdateRequest::Parser::Init(const nl::Weave::TLV::TLVReader & aReader)
{

    WEAVE_ERROR err = WEAVE_NO_ERROR;

    VerifyOrExit(nl::Weave::TLV::AnonymousTag == aReader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);

    err = DataElement::Parser::Init(aReader);
    SuccessOrExit(err);

    mReader.ImplicitProfileId = kWeaveProfile_DictionaryKey;

exit:
    WeaveLogFunctError(err);
    return err;
}

#if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK
WEAVE_ERROR UpdateRequest::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;
    DataList::Parser dataList;

    struct TagPresence
    {
        bool ExpiryTime;
        bool Argument;
        bool DataList;
        bool Authenticator;
        bool UpdateRequestIndex;
    };

    TagPresence tagPresence = { 0 };

    PRETTY_PRINT("{");

    // make a copy of the reader
    reader.Init(mReader);

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        // Authenticators carry profile tags
        const uint64_t tag = reader.GetTag();

        if (nl::Weave::TLV::IsContextTag(tag))
        {
            switch (nl::Weave::TLV::TagNumFromTag(tag))
            {
            case kCsTag_ExpiryTime:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.ExpiryTime == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.ExpiryTime = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    int64_t value;
                    err = reader.Get(value);
                    SuccessOrExit(err);

                    PRETTY_PRINT("\tExpiry Time = 0x%" PRIx64 ",", static_cast<uint64_t>(value));
                }
#endif // WEAVE_DETAIL_LOGGING

                break;

            case kCsTag_UpdateRequestIndex:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.UpdateRequestIndex == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.UpdateRequestIndex = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_UnsignedInteger == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    uint32_t value;
                    err = reader.Get(value);
                    SuccessOrExit(err);

                    PRETTY_PRINT("\tUpdateRequestIndex = 0x%" PRIx32 ",", static_cast<uint32_t>(value));
                }
#endif // WEAVE_DETAIL_LOGGING

                break;

            case kCsTag_Argument:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.Argument == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.Argument = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    PRETTY_PRINT("\t(Argument)");

                    err = ParseData(reader, 0);
                    SuccessOrExit(err);
                }
#endif // WEAVE_DETAIL_LOGGING

                break;

            case kCsTag_DataList:
                // check if this tag has appeared before
                VerifyOrExit(tagPresence.Argument == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.Argument = true;

                VerifyOrExit(nl::Weave::TLV::kTLVType_Array == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

#if WEAVE_DETAIL_LOGGING
                {
                    dataList.Init(reader);

                    PRETTY_PRINT_INCDEPTH();

                    err = dataList.CheckSchemaValidity();
                    SuccessOrExit(err);

                    PRETTY_PRINT_DECDEPTH();
                }
#endif // WEAVE_DETAIL_LOGGING

                break;
            default:
                WeaveLogDetail(DataManagement, "UNKONWN, IGNORE");
            }
        }
        else if (nl::Weave::TLV::IsProfileTag(tag))
        {
            // we cannot use the normal case statement, for ProfileTag is a function instead of a macro
            if (tag ==
                nl::Weave::TLV::ProfileTag(nl::Weave::Profiles::kWeaveProfile_Security,
                                           nl::Weave::Profiles::Security::kTag_WeaveSignature))
            {
                // certificate signature

                VerifyOrExit(tagPresence.Authenticator == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.Authenticator = true;

#if WEAVE_DETAIL_LOGGING
                {
                    PRETTY_PRINT("\t(Authenticator-Certificate)");

                    err = ParseData(reader, 0);
                    SuccessOrExit(err);
                }
#endif // WEAVE_DETAIL_LOGGING
            }
            // we cannot use the normal case statement, for ProfileTag is a function instead of a macro
            // 10 is a place holder for the new group key feature in security profile
            else if (tag ==
                     nl::Weave::TLV::ProfileTag(nl::Weave::Profiles::kWeaveProfile_Security,
                                                nl::Weave::Profiles::Security::kTag_GroupKeySignature))
            {
                // group key signature

                VerifyOrExit(tagPresence.Authenticator == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                tagPresence.Authenticator = true;

#if WEAVE_DETAIL_LOGGING
                {
                    PRETTY_PRINT("\t(Authenticator-Group Key)");

                    err = ParseData(reader, 0);
                    SuccessOrExit(err);
                }
#endif // WEAVE_DETAIL_LOGGING
            }
        }
        else
        {
            // an UpdateRequest can only contain, at top level, context-specific tags or profile tags
            ExitNow(err = WEAVE_ERROR_INVALID_TLV_TAG);
        }
    }

    PRETTY_PRINT("}");
    PRETTY_PRINT("");

    if (WEAVE_END_OF_TLV == err)
    {
        err = WEAVE_NO_ERROR;
    }

exit:
    WeaveLogFunctError(err);

    return err;
}
#endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK

WEAVE_ERROR UpdateRequest::Parser::GetExpiryTimeMicroSecond(int64_t * const apExpiryTimeMicroSecond) const
{
    return GetSimpleValue(kCsTag_ExpiryTime, nl::Weave::TLV::kTLVType_UnsignedInteger, apExpiryTimeMicroSecond);
}

WEAVE_ERROR UpdateRequest::Parser::GetReaderOnArgument(nl::Weave::TLV::TLVReader * const apReader) const
{
    return GetReaderOnTag(nl::Weave::TLV::ContextTag(kCsTag_Argument), apReader);
}

WEAVE_ERROR UpdateRequest::Parser::GetUpdateRequestIndex(uint32_t * const apUpdateRequestIndex) const
{
    return GetSimpleValue(kCsTag_UpdateRequestIndex, nl::Weave::TLV::kTLVType_UnsignedInteger, apUpdateRequestIndex);
}

// Get a TLVReader for the Paths. Next() must be called before accessing them.
WEAVE_ERROR UpdateRequest::Parser::GetDataList (DataList::Parser * const apDataList) const
{
    return apDataList->InitIfPresent(mReader, kCsTag_DataList);
}

// aReader has to be on the element of anonymous container
WEAVE_ERROR UpdateResponse::Parser::Init(const nl::Weave::TLV::TLVReader & aReader)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    // make a copy of the reader here
    mReader.Init(aReader);

    VerifyOrExit(nl::Weave::TLV::AnonymousTag == mReader.GetTag(), err = WEAVE_ERROR_INVALID_TLV_TAG);
    VerifyOrExit(nl::Weave::TLV::kTLVType_Structure == mReader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);

    // This is just a dummy, as we're not going to exit this container ever
    nl::Weave::TLV::TLVType OuterContainerType;
    err = mReader.EnterContainer(OuterContainerType);

exit:
    WeaveLogFunctError(err);
    return err;
}

WEAVE_ERROR UpdateResponse::Parser::CheckSchemaValidity(void) const
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    nl::Weave::TLV::TLVReader reader;
    StatusList::Parser statusList;
    VersionList::Parser versionList;
    struct TagPresence
    {
        bool StatusList;
        bool VersionList;
    };

    TagPresence tagPresence = { 0 };

    reader.Init(mReader);

    PRETTY_PRINT_CHECKPOINT();

    PRETTY_PRINT("{");

    while (WEAVE_NO_ERROR == (err = reader.Next()))
    {
        const uint64_t tag = reader.GetTag();

        if (nl::Weave::TLV::IsContextTag(tag))
        {
            switch (nl::Weave::TLV::TagNumFromTag(tag))
            {
                case kCsTag_StatusList:
                    VerifyOrExit(tagPresence.StatusList == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                    tagPresence.StatusList = true;
                    VerifyOrExit(nl::Weave::TLV::kTLVType_Array == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);
                    {
                        statusList.Init(reader);

                        PRETTY_PRINT_INCDEPTH();

                        err = statusList.CheckSchemaValidity();
                        SuccessOrExit(err);

                        PRETTY_PRINT_DECDEPTH();
                    }
                    break;

                case kCsTag_VersionList:
                    VerifyOrExit(tagPresence.VersionList == false, err = WEAVE_ERROR_INVALID_TLV_TAG);
                    tagPresence.VersionList = true;
                    VerifyOrExit(nl::Weave::TLV::kTLVType_Array == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);
                    {
                        versionList.Init(reader);

                        PRETTY_PRINT_INCDEPTH();

                        err = versionList.CheckSchemaValidity();
                        SuccessOrExit(err);

                        PRETTY_PRINT_DECDEPTH();
                    }
                    break;

                default:
                    WeaveLogDetail(DataManagement, "UNKONWN, IGNORE");
                    break;
            }
        }
    }

    if (WEAVE_END_OF_TLV == err)
    {
        if (tagPresence.VersionList && tagPresence.StatusList)
        {
            err = WEAVE_NO_ERROR;
        }
    }

    PRETTY_PRINT("}");
    PRETTY_PRINT("");

exit:
    WeaveLogFunctError(err);
    return err;
}

// Get a TLVReader for the Status. Next() must be called before accessing them.
WEAVE_ERROR UpdateResponse::Parser::GetStatusList(StatusList::Parser * const apStatusList) const
{
    return apStatusList->InitIfPresent(mReader, kCsTag_StatusList);
}

// Get a TLVReader at the Versions. Next() must be called before accessing it.
// WEAVE_END_OF_TLV if there is no such element
// WEAVE_ERROR_WRONG_TLV_TYPE if there is such element but it's not one of the right types
WEAVE_ERROR UpdateResponse::Parser::GetVersionList(VersionList::Parser * const apVersionList) const
{
    return apVersionList->InitIfPresent(mReader, kCsTag_VersionList);
}

WEAVE_ERROR UpdateResponse::Builder::Init(nl::Weave::TLV::TLVWriter * const apWriter)
{
    return InitAnonymousStructure(apWriter);
}

VersionList::Builder & UpdateResponse::Builder::CreateVersionListBuilder()
{
    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mVersionListBuilder.ResetError(mError));

    mError = mVersionListBuilder.Init(mpWriter, kCsTag_VersionList);
    WeaveLogFunctError(mError);

exit:

    // on error, mVersionListBuilder would be un-/partial initialized and cannot be used to write anything
    return mVersionListBuilder;
}

StatusList::Builder & UpdateResponse::Builder::CreateStatusListBuilder()
{
    // Check if the VersionList::Builder has failed, or has not been used yet
    if (WEAVE_NO_ERROR == mError)
    {
        mError = mVersionListBuilder.GetError();
    }

    // skip if error has already been set
    VerifyOrExit(WEAVE_NO_ERROR == mError, mStatusListBuilder.ResetError(mError));

    mError = mStatusListBuilder.Init(mpWriter, kCsTag_StatusList);
    WeaveLogFunctError(mError);

exit:

    // on error, mStatusListBuilder would be un-/partial initialized and cannot be used to write anything
    return mStatusListBuilder;
}

UpdateResponse::Builder & UpdateResponse::Builder::EndOfResponse(void)
{
    // Check if the StatusList::Builder has failed, or has not been used
    if (WEAVE_NO_ERROR == mError)
    {
        mError = mStatusListBuilder.GetError();
    }
    SuccessOrExit(mError);

    EndOfContainer();

exit:
    return *this;
}

}; // namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current)
}; // namespace Profiles
}; // namespace Weave
}; // namespace nl