text: /*
   *
   *    Copyright (c) 2015-2017 Nest Labs, Inc.
   *    All rights reserved.
   *
   *    Licensed under the Apache License, Version 2.0 (the "License");
   *    you may not use this file except in compliance with the License.
   *    You may obtain a copy of the License at
   *
   *        http://www.apache.org/licenses/LICENSE-2.0
   *
   *    Unless required by applicable law or agreed to in writing, software
   *    distributed under the License is distributed on an "AS IS" BASIS,
   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *    See the License for the specific language governing permissions and
   *    limitations under the License.
   */
  
  /**
   *  @file
   *
   *  @brief
   *    Implementations for the abstract ProfileDatabase auxiliary
   *    class.
   *
   *  This file implements method definitions for the abstract Weave
   *  ProfileDatabase auxiliary class.
   */
  
  #include <Weave/Profiles/data-management/Legacy/WdmManagedNamespace.h>
  
  #include <stdarg.h>
  #include <Weave/Support/CodeUtils.h>
  
  #include <Weave/Profiles/data-management/ProfileDatabase.h>
  
  namespace nl {
  namespace Weave {
  namespace Profiles {
  namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Legacy) {
  
  using namespace ::nl;
  using namespace ::nl::Inet;
  using namespace ::nl::Weave;
  using namespace ::nl::Weave::TLV;
  using namespace ::nl::Weave::Profiles;
  using namespace ::nl::Weave::Profiles::Common;
  
  /**
   *  @brief
   *    Check a WDM-specific tag.
   *
   *  Check the number of a WDM-specific tag, from
   *  .../data-management/DMConstants.h, against the actual tag at the
   *  head of a TLV reader.
   *
   *  @param [in]     aTagNum         The tag number to be checked
   *                                  against a specific TLV element.
   *
   *  @param [in]     aReader         A reference to a TLV reader
   *                                  pointing to the element to be
   *                                  checked.
   *
   *  @return true iff aReader.GetTag() produces a tag that matches one
   *  of the expected tag forms for the given tag number.
   */
  
  bool CheckWDMTag(uint32_t aTagNum, nl::Weave::TLV::TLVReader &aReader)
  {
      bool returnVal;
      uint64_t tag = aReader.GetTag();
  
      switch (aTagNum)
      {
      case kTag_WDMPathList:
      case kTag_WDMPathProfile:
      case kTag_WDMPathArrayIndexSelector:
      case kTag_WDMPathArrayValueSelector:
      case kTag_WDMDataList:
  
          returnVal = (tag == CommonTag(aTagNum) || tag == ProfileTag(kWeaveProfile_WDM, aTagNum));
  
          break;
      case kTag_WDMPathProfileId:
  
          returnVal = (tag == CommonTag(kTag_WDMPathProfileId_Deprecated) ||
                       tag == ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileId_Deprecated) ||
                       tag == ContextTag(aTagNum));
  
          break;
      case kTag_WDMPathProfileInstance:
  
          returnVal = (tag == CommonTag(kTag_WDMPathProfileInstance_Deprecated) ||
                       tag == ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileInstance_Deprecated) ||
                       tag == ContextTag(aTagNum));
  
          break;
      case kTag_WDMDataListElementPath:
  
          returnVal = (tag == CommonTag(kTag_WDMDataListElementPath_Deprecated) ||
                       tag == ProfileTag(kWeaveProfile_WDM, kTag_WDMDataListElementPath_Deprecated) ||
                       tag == ContextTag(aTagNum));
  
          break;
      case kTag_WDMDataListElementVersion:
  
          returnVal = (tag == CommonTag(kTag_WDMDataListElementVersion_Deprecated) ||
                       tag == ProfileTag(kWeaveProfile_WDM, kTag_WDMDataListElementVersion_Deprecated) ||
                       tag == ContextTag(aTagNum));
  
          break;
      case kTag_WDMDataListElementData:
  
          /*
           * this is an especially gnarly one. in the bad old days, we
           * weren't checking this tag and it allowed the service to
           * send a profile tag for the application protocol of interest
           * as the tag for the data container here. this is incorrect,
           * of course, but for purposes of backwards compatibility we
           * should allow it until we're pretty sure the service is up
           * to date.
           */
  
          returnVal = (tag == CommonTag(kTag_WDMDataListElementData_Deprecated) ||
                       tag == ProfileTag(kWeaveProfile_WDM, kTag_WDMDataListElementData_Deprecated) ||
                       tag == ContextTag(aTagNum) ||
                       tag == ProfileTag(kWeaveProfile_NestProtect, 0) ||
                       tag == ProfileTag(kWeaveProfile_Occupancy, 0) ||
                       tag == ProfileTag(kWeaveProfile_Structure, 0) ||
                       tag == ProfileTag(kWeaveProfile_Safety, 0) ||
                       tag == ProfileTag(kWeaveProfile_SafetySummary, 0) ||
                       tag == ProfileTag(kWeaveProfile_NestThermostat, 0));
  
          break;
      default:
  
          returnVal = false;
  
          break;
      }
  
      return returnVal;
  };
  
  /**
   *  @brief
   *    Start reading a path list.
   *
   *  Given a fresh reader and a path list, start reading the list and
   *  validate the tags and types initially encountered in the
   *  process. If all goes well, the reader stops after the list
   *  container is entered.
   *
   *  @param [in]     aPathList       A path list passed as a reference
   *                                  to a ReferencedTLVData object. The
   *                                  normal use case will be where the
   *                                  list is actually still in a buffer
   *                                  after receipt.
   *
   *  @param [out]    aReader         A reference to a TLV reader used
   *                                  to read the path list. This reader
   *                                  will be left pointing just before
   *                                  the first path in the list.
   *
   *  @return #WEAVE_NO_ERROR on success; otherwise, a
   *  #WEAVE_ERROR reflecting a failure the open the path list and/or
   *  validate the relevant tags and types.
   */
  
  WEAVE_ERROR OpenPathList(ReferencedTLVData &aPathList, nl::Weave::TLV::TLVReader &aReader)
  {
      WEAVE_ERROR err;
      TLVType container;
  
      aReader.Init(aPathList.theData, aPathList.theLength);
  
      // we should be looking at an array called a WDM path list
  
      err = aReader.Next();
      SuccessOrExit(err);
  
      err = ValidateTLVType(kTLVType_Array, aReader);
      SuccessOrExit(err);
  
      err = ValidateWDMTag(kTag_WDMPathList, aReader);
      SuccessOrExit(err);
  
      err = aReader.EnterContainer(container);
      SuccessOrExit(err);
  
  exit:
      return err;
  }
  
  /**
   *  @brief
   *    Start reading a data list.
   *
   *  Given a fresh reader and a data list, start reading the list and
   *  validate the tags and types initially encountered in the
   *  process. If all goes well, the reader stops after the list
   *  container is entered.
   *
   *  @param [in]     aDataList       A data list passed as a reference
   *                                  to a ReferencedTLVData object. The
   *                                  normal use case will be where the
   *                                  list is actually still in a buffer
   *                                  after receipt.
   *
   *  @param [out]    aReader         A reference to a TLV reader used
   *                                  to read the data list. This reader
   *                                  will be left pointing just before
   *                                  the first item in the list.
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR reflecting a failure the open the data list and/or
   *  validate the relevant tags and types.
   */
  
  WEAVE_ERROR OpenDataList(ReferencedTLVData &aDataList, nl::Weave::TLV::TLVReader &aReader)
  {
      WEAVE_ERROR err;
      TLVType container;
  
      aReader.Init(aDataList.theData, aDataList.theLength);
  
      // we should be looking at an array called a WDM data list
  
      err = aReader.Next();
      SuccessOrExit(err);
  
      err = ValidateTLVType(kTLVType_Array, aReader);
      SuccessOrExit(err);
  
      err = ValidateWDMTag(kTag_WDMDataList, aReader);
      SuccessOrExit(err);
  
      err = aReader.EnterContainer(container);
      SuccessOrExit(err);
  
  exit:
      return err;
  }
  
  /**
   *  @brief
   *    Start reading a data list element.
   *
   *  Given a reader positioned at a data list element, start reading the
   *  element and validate the tags and types initially encountered in
   *  the process. If all goes well, the reader ends up positioned at the
   *  data element data and the in/out path reader is positioned at the
   *  corresponding path.
   *
   *  @param [in]     aReader         A reference to a TLV reader
   *                                  positioned at a data list element.
   *
   *  @param [out]    aPathReader     A reference to a TLV reader to be
   *                                  pointed at the path component of
   *                                  the data list element.
   *
   *  @param [out]    aVersion        A reference to a 64-bit integer
   *                                  to be set either to the data list
   *                                  element version if one is present
   *                                  or else to kVersionNotSpecified.
   *
   *  @return #WEAVE_NO_ERROR on success or else a #WEAVE_ERROR
   *  associated with opening and reading the data list element.
   */
  
  WEAVE_ERROR OpenDataListElement(nl::Weave::TLV::TLVReader &aReader, nl::Weave::TLV::TLVReader &aPathReader, uint64_t &aVersion)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      TLVType elementContainer;
  
      /*
       * a data list element should be an anonymous structure with 3
       * components, one of which (the version) is optional.
       */
  
      VerifyOrExit(aReader.GetTag() == AnonymousTag, err = WEAVE_ERROR_INVALID_TLV_TAG);
  
      err = ValidateTLVType(kTLVType_Structure, aReader);
      SuccessOrExit(err);
  
      err = aReader.EnterContainer(elementContainer);
      SuccessOrExit(err);
  
      /*
       * first get a copy of the reader that points to the
       * path and hold it for later.
       */
  
      err = aReader.Next();
      SuccessOrExit(err);
  
      VerifyOrExit((aReader.GetType() == kTLVType_Path), err = WEAVE_ERROR_WRONG_TLV_TYPE);
  
      aPathReader = aReader;
  
      /*
       * now get the version if there is one.
       */
  
      err = aReader.Next();
      SuccessOrExit(err);
  
      if (CheckWDMTag(kTag_WDMDataListElementVersion, aReader))
      {
          err = ValidateTLVType(kTLVType_UnsignedInteger, aReader);
          SuccessOrExit(err);
  
          aReader.Get(aVersion);
  
          err = aReader.Next();
          SuccessOrExit(err);
      }
  
      else
      {
          aVersion = kVersionNotSpecified;
      }
  
      err = ValidateWDMTag(kTag_WDMDataListElementData, aReader);
  
  exit:
      return err;
  }
  
  /*
   * @brief
   *   An internal method to start encoding a path list
   *
   * @note
   *   There are a bunch of EncodePath() functions below that differ in
   *   the instance ID and tag style they use. Furthermore, they're all
   *   variadic so there's a chunk of them - the arg unpacking - that's
   *   hard to reduce. These static functions are the best bet for both
   *   reducing the resultant code size and duplication. They knock a few
   *   hundred bytes off the image and at least reduce the absolute amount
   *   of duplicated text by a bit.
   *
   *   This method starts a container with ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfile)
   *   and then insert a context-specific tag with ContextTag(kTag_WDMPathProfileId).
   *   This is the latest version for encoding a path.
   */
  
  static WEAVE_ERROR StartEncodePath(TLVWriter &aWriter,
                                     const uint64_t &aTag,
                                     uint32_t aProfileId,
                                     TLVType &mOuterContainer,
                                     TLVType &mPath)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
  
      err = aWriter.StartContainer(aTag, kTLVType_Path, mOuterContainer);
      SuccessOrExit(err);
  
      // open a structure container and write the profile ID
  
      err = aWriter.StartContainer(ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfile), kTLVType_Structure, mPath);
      SuccessOrExit(err);
  
      err = aWriter.Put(ContextTag(kTag_WDMPathProfileId), aProfileId);
      SuccessOrExit(err);
  
      exit:
  
      return err;
  }
  
  static WEAVE_ERROR EndEncodePath(TLVWriter &aWriter, TLVType &mOuterContainer, WEAVE_ERROR mError)
  {
      WEAVE_ERROR retErr = aWriter.EndContainer(mOuterContainer);
  
      /*
       * take the "worst" error and return it.
       */
  
      if (mError != WEAVE_NO_ERROR)
          retErr = mError;
  
      return retErr;
  }
  
  /**
   *  @brief
   *    Encode a WDM path with an integer profile instance ID.
   *
   *  @note
   *    Write a TLV path of the kind used in data management where, in
   *    particular, there is a profile designation placed at the beginning
   *    in order to allow interpretation of subsequent path elements.
   *
   *    This version of the method takes an integer profile instance ID.
   *
   *    This method inserts instance ID using ContextTag(kTag_WDMPathProfileInstance),
   *    which is the latest version for encoding a path.
   *
   *  @param [in]     aWriter         A reference to the TLV writer used
   *                                  to write out the path.
   *
   *  @param [in]     aTag            A reference to the fully-qualified
   *                                  TLV tag that applies to this path.
   *
   *  @param [in]     aProfileId      The profile ID under which
   *                                  elements of the path are to be
   *                                  interpreted.
   *
   *  @param [in]     aInstanceId     A reference to the optional
   *                                  instance identifier of the profile
   *                                  to be used. If no instance ID is
   *                                  to be used then this parameter
   *                                  should have a value of
   *                                  kInstanceIdNotSpecified.
   *
   *  @param [in]     aPathLen        The, possibly 0, length of the
   *                                  list of path elements beyond the
   *                                  initial profile specifier.
   *
   *  @param [in]     ...             The optional variable-length list
   *                                  of additional path tags.
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR reflecting an inability to format the given path.
   */
  
  WEAVE_ERROR EncodePath(TLVWriter &aWriter,
                         const uint64_t &aTag,
                         uint32_t aProfileId,
                         const uint64_t &aInstanceId,
                         uint16_t aPathLen,
                         ...)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      va_list pathTags;
      TLVType outerContainer;
      TLVType path;
  
      err = StartEncodePath(aWriter, aTag, aProfileId, outerContainer, path);
      SuccessOrExit(err);
  
      if (aInstanceId != kInstanceIdNotSpecified)
      {
          err = aWriter.Put(ContextTag(kTag_WDMPathProfileInstance), aInstanceId);
          SuccessOrExit(err);
      }
  
      err = aWriter.EndContainer(path);
      SuccessOrExit(err);
  
      va_start(pathTags, aPathLen);
  
      for (int i = 0; i < aPathLen; i++)
      {
          err = aWriter.PutNull(va_arg(pathTags, uint64_t));
  
          if (err != WEAVE_NO_ERROR)
              break;
      }
  
      va_end(pathTags);
  
  exit:
  
      return EndEncodePath(aWriter, outerContainer, err);
  }
  
  /**
   *  @brief
   *    Encode a WDM path with a byte array instance ID.
   *
   *  @note
   *    Write a TLV path of the kind used in data management where, in
   *    particular, there is a profile designation placed at the beginning
   *    in order to allow interpretation of subsequent path elements.
   *
   *    This version of the method takes a byte-array profile instance ID
   *    along with a length.
   *
   *    This method inserts instance ID using ContextTag(kTag_WDMPathProfileInstance),
   *    which is the latest version for encoding a path.
   *
   *  @param [in]     aWriter         A reference to the TLV writer used
   *                                  to write out the path.
   *
   *  @param [in]     aTag            A reference to the fully-qualified
   *                                  TLV tag that applies to this path.
   *
   *  @param [in]     aProfileId      The profile ID under which the
   *                                  elements of the path are to be
   *                                  interpreted.
   *
   *  @param [in]     aInstanceIdLen  The length of the byte array
   *                                  that constitutes the instance
   *                                  ID. If there is no ID then this
   *                                  parameter shall have a value of 0.
   *
   *  @param [in]     aInstanceId     The optional byte array used as a
   *                                  profile instance identifier. This
   *                                  argument may be NULL in the case
   *                                  where no instance ID is specified.
   *
   *  @param [in]     aPathLen        The, possibly 0, length of the
   *                                  list of path elements beyond the
   *                                  initial profile specifier.
   *
   *  @param [in]     ...             The optional, variable-length list
   *                                  of additional path tags.
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR reflecting an inability to format the given path.
   */
  
  WEAVE_ERROR EncodePath(TLVWriter &aWriter,
                         const uint64_t &aTag,
                         uint32_t aProfileId,
                         const uint32_t aInstanceIdLen,
                         const uint8_t *aInstanceId,
                         uint16_t aPathLen,
                         ...)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      va_list pathTags;
      TLVType outerContainer;
      TLVType path;
  
      err = StartEncodePath(aWriter, aTag, aProfileId, outerContainer, path);
      SuccessOrExit(err);
  
      if (aInstanceId)
      {
          err = aWriter.PutBytes(ContextTag(kTag_WDMPathProfileInstance), aInstanceId, aInstanceIdLen);
          SuccessOrExit(err);
      }
  
      err = aWriter.EndContainer(path);
      SuccessOrExit(err);
  
      va_start(pathTags, aPathLen);
  
      for (int i = 0; i < aPathLen; i++)
      {
          err = aWriter.PutNull(va_arg(pathTags, uint64_t));
  
          if (err != WEAVE_NO_ERROR)
              break;
      }
  
      va_end(pathTags);
  
  exit:
  
      return EndEncodePath(aWriter, outerContainer, err);
  }
  
  /**
   *  @brief
   *    Encode a WDM path with a string instance ID.
   *
   *  @note
   *    Write a TLV path of the kind used in data management where, in
   *    particular, there is a profile designation placed at the beginning
   *    in order to allow interpretation of subsequent path elements.
   *
   *    This version of the method takes a string profile instance ID.
   *
   *    This method inserts instance ID using ContextTag(kTag_WDMPathProfileInstance),
   *    which is the latest version for encoding a path.
   *
   *  @param [in]     aWriter         A reference to the TLV writer used
   *                                  to write out the path.
   *
   *  @param [in]     aTag            A reference to the fully-qualified
   *                                  TLV tag that applies to this path.
   *
   *  @param [in]     aProfileId      The profile ID under which
   *                                  elements of the path are to be
   *                                  interpreted.
   *
   *  @param [in]     aInstanceId     The optional string used as a
   *                                  profile instance identifier. This
   *                                  argument may be NULL if no
   *                                  instance ID is specified.
   *
   *  @param [in]     aPathLen        The, possibly 0, length of the
   *                                  list of path elements beyond the
   *                                  initial profile specifier.
   *
   *  @param [in]     ...             The optional, variable-length list
   *                                  of additional path tags.
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR reflecting an inability to format the given path.
   */
  
  WEAVE_ERROR EncodePath(TLVWriter &aWriter,
                         const uint64_t &aTag,
                         uint32_t aProfileId,
                         const char *aInstanceId,
                         uint16_t aPathLen,
                         ...)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      va_list pathTags;
      TLVType outerContainer;
      TLVType path;
  
      err = StartEncodePath(aWriter, aTag, aProfileId, outerContainer, path);
      SuccessOrExit(err);
  
      if (aInstanceId)
      {
          err = aWriter.PutString(ContextTag(kTag_WDMPathProfileInstance), aInstanceId);
          SuccessOrExit(err);
      }
  
      err = aWriter.EndContainer(path);
      SuccessOrExit(err);
  
      va_start(pathTags, aPathLen);
  
      for (int i = 0; i < aPathLen; i++)
      {
          err = aWriter.PutNull(va_arg(pathTags, uint64_t));
  
          if (err != WEAVE_NO_ERROR)
              break;
      }
  
      va_end(pathTags);
  
  exit:
  
      return EndEncodePath(aWriter, outerContainer, err);
  }
  
  /**
   *  @brief
   *    Encode a WDM path with deprecated tags and an integer instance ID.
   *
   *  @note
   *    Encode a path using the deprecated tag set accepted by the service
   *    before Weave release 2.0. This version of the method takes a numerical
   *    instance identifier.
   *
   *   This method starts a container with ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfile)
   *   and then inserts the profile ID with ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileId_Deprecated).
   *   It then inserts the instance ID with ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileInstance_Deprecated).
   *   This is one of the deprecated versions for encoding a path, and new designs shall avoid
   *   using this format.
   *
   *  @param [in]     aWriter         A reference to the TLV writer used
   *                                  to write out the path.
   *
   *  @param [in]     aTag            A reference to the fully-qualified
   *                                  TLV tag that applies to this path.
   *
   *  @param [in]     aProfileId      The profile ID under which
   *                                  elements of the path are to be
   *                                  interpreted.
   *
   *  @param [in]     aInstanceId     A reference to the optional
   *                                  instance ID of the profile to be
   *                                  used.
   *
   *  @param [in]     aPathLen        The, possibly 0, length of the
   *                                  list of path elements beyond the
   *                                  initial profile specifier.
   *
   *  @param [in]     ...             The optional variable-length list
   *                                  of additional path tags.
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR reflecting an inability to format the given path.
   */
  
  WEAVE_ERROR EncodeDeprecatedPath(TLVWriter &aWriter,
                                   const uint64_t &aTag,
                                   uint32_t aProfileId,
                                   const uint64_t &aInstanceId,
                                   uint16_t aPathLen,
                                   ...)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      va_list pathTags;
      TLVType outerContainer;
      TLVType path;
  
      err = aWriter.StartContainer(aTag, kTLVType_Path, outerContainer);
      SuccessOrExit(err);
  
      // write the profile and instance (if specified)
  
      err = aWriter.StartContainer(ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfile), kTLVType_Structure, path);
      SuccessOrExit(err);
  
      err = aWriter.Put(ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileId_Deprecated), aProfileId);
      SuccessOrExit(err);
  
      if (aInstanceId != kInstanceIdNotSpecified)
      {
          err = aWriter.Put(ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileInstance_Deprecated), aInstanceId);
          SuccessOrExit(err);
      }
  
      err = aWriter.EndContainer(path);
      SuccessOrExit(err);
  
      va_start(pathTags, aPathLen);
  
      for (int i = 0; i < aPathLen; i++)
      {
          err = aWriter.PutNull(va_arg(pathTags, uint64_t));
  
          if (err != WEAVE_NO_ERROR)
              break;
      }
  
      va_end(pathTags);
  
  exit:
  
      return EndEncodePath(aWriter, outerContainer, err);
  }
  
  /**
   *  @brief
   *    Encode a WDM path with deprecated tags and a string instance ID.
   *
   *  @note
   *    Encode a path using the deprecated tag set (see DMConstants.h). This
   *    version of the method takes an instance ID string.
   *
   *   This method starts a container with ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfile)
   *   and then inserts the profile ID with ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileId_Deprecated).
   *   It then inserts the instance ID with ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileInstance_Deprecated).
   *   This is one of the deprecated versions for encoding a path, and new designs shall avoid
   *   using this format.
   *
   *  @param [in]     aWriter         A reference to the TLV writer used
   *                                  to write out the path.
   *
   *  @param [in]     aTag            A reference to the fully-qualified
   *                                  TLV tag that applies to this path.
   *
   *  @param [in]     aProfileId      The profile ID under which
   *                                  elements of the path are to be
   *                                  interpreted.
   *
   *  @param [in]     aInstanceId     The optional string used as a
   *                                  profile instance identifier. This
   *                                  argument may be NULL if no
   *                                  instance ID is specified.
   *
   *  @param [in]     aPathLen        The, possibly 0, length of the
   *                                  list of path elements beyond the
   *                                  initial profile specifier.
   *
   *  @param [in]     ...             The optional, variable-length list
   *                                  of additional path tags.
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR reflecting an inability to format the given path.
   */
  
  WEAVE_ERROR EncodeDeprecatedPath(TLVWriter &aWriter,
                                   const uint64_t &aTag,
                                   uint32_t aProfileId,
                                   const char *aInstanceId,
                                   uint16_t aPathLen,
                                   ...)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      va_list pathTags;
      TLVType outerContainer;
      TLVType path;
  
      err = aWriter.StartContainer(aTag, kTLVType_Path, outerContainer);
      SuccessOrExit(err);
  
      // write the profile and instance (if specified)
  
      err = aWriter.StartContainer(ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfile), kTLVType_Structure, path);
      SuccessOrExit(err);
  
      err = aWriter.Put(ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileId_Deprecated), aProfileId);
      SuccessOrExit(err);
  
      if (aInstanceId != NULL)
      {
          err = aWriter.PutString(ProfileTag(kWeaveProfile_WDM, kTag_WDMPathProfileInstance_Deprecated), aInstanceId);
          SuccessOrExit(err);
      }
  
      err = aWriter.EndContainer(path);
      SuccessOrExit(err);
  
      va_start(pathTags, aPathLen);
  
      for (int i = 0; i < aPathLen; i++)
      {
          err = aWriter.PutNull(va_arg(pathTags, uint64_t));
  
          if (err != WEAVE_NO_ERROR)
              break;
      }
  
      va_end(pathTags);
  
  exit:
  
      return EndEncodePath(aWriter, outerContainer, err);
  }
  
  /*
   * in both of the following methods we set the version to 0.  we're
   * assuming that, for our purposes, it's OK to start the version at 0
   * and count up. if something else is required, it can be handled in
   * the concrete subclass.
   */
  
  /**
   *  @brief
   *    The default constructor for ProfileData.
   *
   *  Initialize a fresh ProfileData item by setting its version to 0.
   */
  
  ProfileDatabase::ProfileData::ProfileData(void)
  {
      mVersion = 0;
  }
  
  /**
   *  @brief
   *    The destructor for ProfileData
   *
   *  Like the constructor this just clears the data verion to 0.
   */
  
  ProfileDatabase::ProfileData::~ProfileData(void)
  {
      mVersion = 0;
  }
  
  /**
   *  @brief
   *    Store a data list item being read.
   *
   *  This virtual method is used to store a particular data list item in
   *  an object of a concrete ProfileData subclass. The implementation
   *  here in the super-class may be used if the object is simple and
   *  "shallow", having only paths that are one element long. For a more
   *  complicated schema, implementers should override this method.
   *
   *  @param [in]     aPathReader     A reference to a TLV reader
   *                                  positioned at the path component
   *                                  of the data list item.
   *
   *  @param [in]     aVersion        The 64-bit version component of
   *                                  the data list item.
   *
   *  @param [in]     aDataReader     A reference to a TLV reader
   *                                  positioned at the data component
   *                                  of the data list item.
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR indicating a failure to store the data of interest.
   */
  
  WEAVE_ERROR ProfileDatabase::ProfileData::Store(TLVReader &aPathReader, uint64_t aVersion, TLVReader &aDataReader)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      TLVType container;
  
      err = aPathReader.Next();
  
      if (err == WEAVE_END_OF_TLV)
      {
          /*
           * the path given was the top-level profile path so the we
           * have to do the entire bucket, as follows. we should be
           * looking at a structure.
           */
  
          VerifyOrExit((aDataReader.GetType() == kTLVType_Structure), err = WEAVE_ERROR_WRONG_TLV_TYPE);
  
          err = aDataReader.EnterContainer(container);
          SuccessOrExit(err);
  
          err = aDataReader.Next();
          while (err == WEAVE_NO_ERROR)
          {
              err = StoreItem(aDataReader.GetTag(), aDataReader);
              SuccessOrExit(err);
  
              err = aDataReader.Next();
          }
  
          // don't return an error if we're just out of TLV
  
          if (err == WEAVE_END_OF_TLV)
              err = WEAVE_NO_ERROR;
  
          aDataReader.ExitContainer(container);
      }
  
      else
      {
          /*
           * in this case, the path contained an additional tag
           * accessing a particular data item directly.
           */
  
          err = StoreItem(aPathReader.GetTag(), aDataReader);
          SuccessOrExit(err);
      }
  
      /*
       * whatever happened above, we set the version here as long as it
       * didn't go horribly wrong.
       */
  
      mVersion = aVersion;
  
  exit:
      return err;
  }
  
  /**
   *  @brief
   *    Store a data list.
   *
   *  Given a TLV-encoded data list, this method goes through the process
   *  of parsing that list and calling the concrete methods provided by
   *  ProfileDatabase subclass implementers to put the referenced data
   *  where it belongs.
   *
   *  @param [in]     aDataList       A reference to a ReferencedTLVData object
   *                                  containing the data of interest in
   *                                  TLV-encoded form.
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR indicating a failure to store the data of interest.
   */
  
  WEAVE_ERROR ProfileDatabase::Store(ReferencedTLVData &aDataList)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      TLVReader dataRdr;
      uint64_t version;
      TLVReader pathRdr;
  
      OpenDataList(aDataList, dataRdr);
  
      err = dataRdr.Next();
      while (err == WEAVE_NO_ERROR)
      {
          err = OpenDataListElement(dataRdr, pathRdr, version);
          SuccessOrExit(err);
  
          err = StoreInternal(pathRdr, version, dataRdr);
          SuccessOrExit(err);
  
          err = CloseDataListElement(dataRdr);
          SuccessOrExit(err);
  
          err = dataRdr.Next();
      }
  
      err = CloseList(dataRdr);
  
  exit:
      return err;
  }
  
  /**
   *  @brief
   *    Retrieve a data list given a path list.
   *
   *  Given a list of paths, retrieve a data list containing data list
   *  elements for each path in the path list the data that is the terminal of that
   *  path.
   *
   *  @param [in]     aPathList       A reference to a ReferencedTLVData
   *                                  object containing a TLV-encoded
   *                                  list of paths representing data to
   *                                  retrieve. This parameter is kept constant
   *                                  throughout the execution of this function.
   *
   *  @param [out]    aDataList       A reference to a ReferencedTLVData
   *                                  object in which to write the
   *                                  retrieved results. Data length is adjusted only
   *                                  after successful execution of this function.
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR indicating a failure to retrieve the data list of
   *  interest.
   */
  
  WEAVE_ERROR ProfileDatabase::Retrieve(ReferencedTLVData &aPathList, ReferencedTLVData &aDataList)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
  
      TLVWriter writer;
      writer.Init(aDataList.theData, aDataList.theMaxLength);
  
      err = Retrieve(aPathList, writer);
      SuccessOrExit(err);
  
      aDataList.theLength = writer.GetLengthWritten();
  
  exit:
      return err;
  }
  
  /**
   *  @brief
   *    Write out a data list given a path list.
   *
   *  Given a list of paths and a TLV writer, write out a data list
   *  containing data list elements for each path in the path list and the data that is the
   *  terminal of that path.
   *
   *  @param [in]     aPathList       A reference to a ReferencedTLVData
   *                                  object containing a list of TLV
   *                                  paths representing data to
   *                                  retrieve. This parameter is kept constant
   *                                  throughout the execution of this function.
   *
   *  @param [in]     aWriter         A reference to the TLV writer to
   *                                  use in writing out the retrieved
   *                                  path list. Internal state for the writer could be
   *                                  unrecoverable in case of an error.
   *
   *
   *  @return #WEAVE_NO_ERROR On success. Otherwise return a
   *  #WEAVE_ERROR indicating a failure to retrieve the data or write
   *  out the data list of interest.
   */
  
  WEAVE_ERROR ProfileDatabase::Retrieve(ReferencedTLVData &aPathList, TLVWriter &aWriter)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      TLVReader rdr;
      ProfileData *profileData;
  
      // aPathList is copied at here into rdr, and then left alone, so
      // it stays constant no matter what will happen
      err = OpenPathList(aPathList, rdr);
      SuccessOrExit(err);
  
      // There is no useful guarantee for the state of aWriter shall anything go wrong
      err = StartDataList(aWriter);
      SuccessOrExit(err);
  
      err = rdr.Next();
      while (err == WEAVE_NO_ERROR)
      {
          TLVType containerTypePathList;
  
          err = rdr.EnterContainer(containerTypePathList);
          SuccessOrExit(err);
  
          err = rdr.Next();
          SuccessOrExit(err);
  
          err = LookupDataFromProfileDescriptor(rdr, &profileData);
          SuccessOrExit(err);
  
          /*
           * OK. now we're poised for victory. there's a reader to read
           * the path. there's a writer to write out the data. as the
           * Nike ads used to say, "Just Do It".
           */
  
          err = StartDataListElement(aWriter);
          SuccessOrExit(err);
  
          err = profileData->Retrieve(rdr, aWriter);
          SuccessOrExit(err);
  
          err = EndDataListElement(aWriter);
          SuccessOrExit(err);
  
          /*
           * now we actually close the path we're working on and move on
           * to the next path list element, if any.
           */
  
          err = rdr.ExitContainer(containerTypePathList);
          SuccessOrExit(err);
  
          err = rdr.Next();
      }
  
      if (err == WEAVE_END_OF_TLV)
          err = WEAVE_NO_ERROR;
  
      EndList(aWriter);
  
      // Note this isn't really necessary, as rdr should already be depleted by now.
      // We reach this line when we get WEAVE_END_OF_TLV from rdr.Next().
      CloseList(rdr);
  
  exit:
  
      return err;
  }
  
  /**
   *  @brief
   *    Find a ProfileData object in the database.
   *
   *  This utility method is used to find ProfileData objects in a
   *  particular ProfileDatabase. It depends largely on lookup methods
   *  provided by the implementer of the concrete ProfileDatabase
   *  subclass.
   *
   *  @note
   *    This is the top-level method for finding the right profile
   *    data structure to match an existing path. The path reader
   *    should be pointing at the profile descriptor, the one immediately
   *    after entering the path container.
   *
   *  @param [in]     aPathReader     A reference to a TLV reader
   *                                  positioned at a WDM path - i.e. a
   *                                  TLV path that has, as its first
   *                                  element, a profile description.
   *
   *  @param [out]    aProfileData    A pointer, intended to return a
   *                                  pointer to the ProfileData object
   *                                  of interest.
   *
   *  @return #WEAVE_NO_ERROR on success, otherwise return a
   *  #WEAVE_ERROR indicating a failure to look up a matching
   *  ProfileData object.
   */
  
  WEAVE_ERROR ProfileDatabase::LookupDataFromProfileDescriptor(nl::Weave::TLV::TLVReader &aDescReader, ProfileData **aProfileData)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
  
      TLVType profileContainer;
  
      uint32_t profileId;
  
      TLVReader instanceIdRdr;
  
      /*
       * the first element of a path under WDM should be a structure
       * with 2 elements, one of which (the instance) is optional.
       */
  
      err = ValidateTLVType(kTLVType_Structure, aDescReader);
      SuccessOrExit(err);
  
      err = ValidateWDMTag(kTag_WDMPathProfile, aDescReader);
      SuccessOrExit(err);
  
      /*
       * parse the path profile and get the profile data object
       */
  
      err = aDescReader.EnterContainer(profileContainer);
      SuccessOrExit(err);
  
      /*
       * the first element here should be a profile ID
       */
  
      err = aDescReader.Next();
      SuccessOrExit(err);
  
      err = ValidateTLVType(kTLVType_UnsignedInteger, aDescReader);
      SuccessOrExit(err);
  
      err = ValidateWDMTag(kTag_WDMPathProfileId, aDescReader);
      SuccessOrExit(err);
  
      err = aDescReader.Get(profileId);
      SuccessOrExit(err);
  
      /*
       * and the second may be an instance. if there's one there, then
       * point the NHL lookup method at a reader for it.
       */
  
      err = aDescReader.Next();
  
      if (err == WEAVE_END_OF_TLV)
      {
          err = LookupProfileData(profileId, NULL, aProfileData);
      }
  
      else if (err == WEAVE_NO_ERROR)
      {
          instanceIdRdr = aDescReader;
  
          err = LookupProfileData(profileId, &instanceIdRdr, aProfileData);
      }
  
      SuccessOrExit(err);
  
      /*
       * now get out. skip over the instance, if any, with the path
       * reader and force an exit from the container with profile info
       * in it.
       */
  
      aDescReader.ExitContainer(profileContainer);
  
  exit:
      return err;
  }
  
  /**
   *  @brief
   *    Find a ProfileData object in the database.
   *
   *  This utility method is used to find ProfileData objects in a
   *  particular ProfileDatabase. It depends largely on lookup methods
   *  provided by the implementer of the concrete ProfileDatabase
   *  subclass.
   *
   *  @note
   *    This is the top-level method for finding the right profile
   *    data structure to match an existing path. The path reader
   *    should be pointing at the whole path container.
   *
   *  @param [in]     aPathReader     A reference to a TLV reader
   *                                  positioned at a WDM path - i.e. a
   *                                  TLV path that has, as its first
   *                                  element, a profile description.
   *
   *  @param [out]    aProfileData    A pointer, intended to return a
   *                                  pointer to the ProfileData object
   *                                  of interest.
   *
   *  @return #WEAVE_NO_ERROR on success, otherwise return a
   *  #WEAVE_ERROR indicating a failure to look up a matching
   *  ProfileData object.
   */
  
  WEAVE_ERROR ProfileDatabase::LookupProfileData(TLVReader &aPathReader, ProfileData **aProfileData)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      TLVType pathContainer;
  
      err = aPathReader.EnterContainer(pathContainer);
      SuccessOrExit(err);
  
      /*
       * the first element of a path under WDM should be a structure
       * with 2 elements, one of which (the instance) is optional.
       */
  
      err = aPathReader.Next();
      SuccessOrExit(err);
  
      err = LookupDataFromProfileDescriptor(aPathReader, aProfileData);
      SuccessOrExit(err);
  
      /*
       * we explicitly DON'T exit the path container here because it may
       * contain additional elements.
       */
  
  exit:
      return err;
  }
  
  WEAVE_ERROR ProfileDatabase::StoreInternal(TLVReader &aPathReader, uint64_t aVersion, TLVReader &aDataReader)
  {
      WEAVE_ERROR err = WEAVE_NO_ERROR;
      ProfileData *profileData;
      TLVType containerTypeDataElem;
  
      err = aPathReader.EnterContainer(containerTypeDataElem);
      SuccessOrExit(err);
  
      err = aPathReader.Next();
      SuccessOrExit(err);
  
      err = LookupDataFromProfileDescriptor(aPathReader, &profileData);
      SuccessOrExit(err);
  
      err = profileData->Store(aPathReader, aVersion, aDataReader);
      SuccessOrExit(err);
  
      err = aPathReader.ExitContainer(containerTypeDataElem);
  
  exit:
      return err;
  }
  
  }; // WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Legacy)
  }; // Profiles
  }; // Weave
  }; // nl
  