Matter SDK Coverage Report
Current view: top level - app - AttributePathExpandIterator.cpp (source / functions) Coverage Total Hit
Test: SHA:5853f10e345717417494f970a7d13b422d94af51 Lines: 99.1 % 106 105
Test Date: 2025-06-30 07:09:23 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2024 Project CHIP Authors
       3              :  *    All rights reserved.
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : #include <app/AttributePathExpandIterator.h>
      18              : 
      19              : #include <app/GlobalAttributes.h>
      20              : #include <app/data-model-provider/MetadataLookup.h>
      21              : #include <app/data-model-provider/MetadataTypes.h>
      22              : #include <lib/core/DataModelTypes.h>
      23              : #include <lib/support/CodeUtils.h>
      24              : #include <lib/support/ReadOnlyBuffer.h>
      25              : 
      26              : #include <optional>
      27              : 
      28              : using namespace chip::app::DataModel;
      29              : 
      30              : namespace chip {
      31              : namespace app {
      32              : 
      33         2494 : AttributePathExpandIterator::AttributePathExpandIterator(DataModel::Provider * dataModel, Position & position) :
      34         2494 :     mDataModelProvider(dataModel), mPosition(position)
      35         2494 : {}
      36              : 
      37        11378 : bool AttributePathExpandIterator::AdvanceOutputPath(std::optional<DataModel::AttributeEntry> * entry)
      38              : {
      39              :     /// Output path invariants
      40              :     ///    - kInvalid* constants are used to define "no value available (yet)" and
      41              :     ///      iteration loop will fill the first value when such a value is seen (fixed for non-wildcard
      42              :     ///      or iteration-based in case of wildcards).
      43              :     ///    - Iteration of the output path is done in order: first endpoint, then cluster, then attribute.
      44              :     /// Processing works like:
      45              :     ///    - Initial state is kInvalidEndpointId/kInvalidClusterId/kInvalidAttributeId
      46              :     ///    - First loop pass fills-in endpointID, followed by clusterID, followed by attributeID
      47              :     ///    - Whenever one level is done iterating (there is no "next") the following
      48              :     ///      "higher path component" is updated:
      49              :     ///         - once a valid path exists, try to advance attributeID
      50              :     ///         - if attributeID fails to advance, try to advance clusterID (and restart attributeID)
      51              :     ///         - if clusterID fails to advance, try to advance endpointID (and restart clusterID)
      52              :     ///         - if endpointID fails to advance, iteration is done
      53              :     while (true)
      54              :     {
      55        11378 :         if (mPosition.mOutputPath.mClusterId != kInvalidClusterId)
      56              :         {
      57         7471 :             std::optional<AttributeId> nextAttribute = NextAttribute(entry);
      58         7471 :             if (nextAttribute.has_value())
      59              :             {
      60         5390 :                 mPosition.mOutputPath.mAttributeId = *nextAttribute;
      61         5390 :                 mPosition.mOutputPath.mExpanded    = mPosition.mAttributePath->mValue.IsWildcardPath();
      62         5390 :                 return true;
      63              :             }
      64              :         }
      65              : 
      66              :         // no valid attribute, try to advance the cluster, see if a suitable one exists
      67         5988 :         if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId)
      68              :         {
      69         4082 :             std::optional<ClusterId> nextCluster = NextClusterId();
      70         4082 :             if (nextCluster.has_value())
      71              :             {
      72              :                 // A new cluster ID is to be processed. This sets the cluster ID to the new value and
      73              :                 // ALSO resets the attribute ID to "invalid", to trigger an attribute set/expansion from
      74              :                 // the beginning.
      75         2086 :                 mPosition.mOutputPath.mClusterId   = *nextCluster;
      76         2086 :                 mPosition.mOutputPath.mAttributeId = kInvalidAttributeId;
      77         2086 :                 continue;
      78              :             }
      79              :         }
      80              : 
      81              :         // No valid cluster, try advance the endpoint, see if a suitable one exists.
      82         3902 :         std::optional<EndpointId> nextEndpoint = NextEndpointId();
      83         3902 :         if (nextEndpoint.has_value())
      84              :         {
      85              :             // A new endpoint ID is to be processed. This sets the endpoint ID to the new value and
      86              :             // ALSO resets the cluster ID to "invalid", to trigger a cluster set/expansion from
      87              :             // the beginning.
      88         2001 :             mPosition.mOutputPath.mEndpointId = *nextEndpoint;
      89         2001 :             mPosition.mOutputPath.mClusterId  = kInvalidClusterId;
      90         2001 :             continue;
      91              :         }
      92         1901 :         return false;
      93         4087 :     }
      94              : }
      95              : 
      96         7051 : bool AttributePathExpandIterator::Next(ConcreteAttributePath & path, std::optional<DataModel::AttributeEntry> * entry)
      97              : {
      98         8952 :     while (mPosition.mAttributePath != nullptr)
      99              :     {
     100         7291 :         if (AdvanceOutputPath(entry))
     101              :         {
     102         5390 :             path = mPosition.mOutputPath;
     103         5390 :             return true;
     104              :         }
     105         1901 :         mPosition.mAttributePath = mPosition.mAttributePath->mpNext;
     106         1901 :         mPosition.mOutputPath    = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId);
     107              :     }
     108              : 
     109         1661 :     return false;
     110              : }
     111              : 
     112         7471 : std::optional<AttributeId> AttributePathExpandIterator::NextAttribute(std::optional<DataModel::AttributeEntry> * entry)
     113              : {
     114         7471 :     if (mPosition.mOutputPath.mAttributeId == kInvalidAttributeId)
     115              :     {
     116              :         // Attribute ID is tied to attribute index. If no attribute id is available yet
     117              :         // this means the index is invalid. Processing logic in output advance only resets
     118              :         // attribute ID to invalid when resetting iteration.
     119         2089 :         mAttributeIndex = kInvalidIndex;
     120              :     }
     121              : 
     122         7471 :     if (mAttributeIndex == kInvalidIndex)
     123              :     {
     124              :         // start a new iteration of attributes on the current cluster path.
     125         3952 :         mAttributes = mDataModelProvider->AttributesIgnoreError(mPosition.mOutputPath);
     126              : 
     127         3952 :         if (mPosition.mOutputPath.mAttributeId != kInvalidAttributeId)
     128              :         {
     129              :             // Position on the correct attribute if we have a start point
     130         1863 :             mAttributeIndex = 0;
     131        14186 :             while ((mAttributeIndex < mAttributes.size()) &&
     132         7091 :                    (mAttributes[mAttributeIndex].attributeId != mPosition.mOutputPath.mAttributeId))
     133              :             {
     134         5232 :                 mAttributeIndex++;
     135              :             }
     136              :         }
     137              :     }
     138              : 
     139         7471 :     if (mPosition.mOutputPath.mAttributeId == kInvalidAttributeId)
     140              :     {
     141         2089 :         if (!mPosition.mAttributePath->mValue.HasWildcardAttributeId())
     142              :         {
     143              :             // The attributeID is NOT a wildcard (i.e. it is fixed).
     144              :             //
     145              :             // For wildcard expansion, we validate that this is a valid attribute for the given
     146              :             // cluster on the given endpoint. If not a wildcard expansion, return it as-is.
     147         1613 :             DataModel::AttributeFinder finder(mDataModelProvider);
     148              : 
     149         1613 :             const ConcreteAttributePath attributePath(mPosition.mOutputPath.mEndpointId, mPosition.mOutputPath.mClusterId,
     150         1613 :                                                       mPosition.mAttributePath->mValue.mAttributeId);
     151         1613 :             std::optional<DataModel::AttributeEntry> foundEntry = finder.Find(attributePath);
     152              : 
     153              :             // if the entry is valid, we can just return it
     154         1613 :             if (foundEntry.has_value())
     155              :             {
     156         1219 :                 if (entry)
     157              :                 {
     158            2 :                     entry->emplace(*foundEntry);
     159              :                 }
     160         1219 :                 return mPosition.mAttributePath->mValue.mAttributeId;
     161              :             }
     162              : 
     163              :             // if the entry is invalid and we are wildcard-expanding, this is not a valid value so
     164              :             // return "not valid"
     165          394 :             if (mPosition.mAttributePath->mValue.IsWildcardPath())
     166              :             {
     167           20 :                 return std::nullopt;
     168              :             }
     169              : 
     170              :             // We get here if all the the conditions below are true:
     171              :             //   - entry is NOT valid (this is not a valid attribute)
     172              :             //   - path is NOT a wildcard (i.e. we were asked to explicitly return it)
     173              :             // as a result, we have no way to generate a "REAL" attribute metadata.
     174              :             // So even though we return a valid attribute id, entry will be empty
     175          374 :             if (entry)
     176              :             {
     177            0 :                 entry->reset();
     178              :             }
     179              :             // forced ID (even if invalid)
     180          374 :             return mPosition.mAttributePath->mValue.mAttributeId;
     181         1613 :         }
     182          476 :         mAttributeIndex = 0;
     183              :     }
     184              :     else
     185              :     {
     186         5382 :         mAttributeIndex++;
     187              :     }
     188              : 
     189              :     // Advance the existing attribute id if it can be advanced.
     190         5858 :     VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardAttributeId(), std::nullopt);
     191              : 
     192         4466 :     if (mAttributeIndex < mAttributes.size())
     193              :     {
     194         3797 :         if (entry != nullptr)
     195              :         {
     196            6 :             entry->emplace(mAttributes[mAttributeIndex]);
     197              :         }
     198         3797 :         return mAttributes[mAttributeIndex].attributeId;
     199              :     }
     200              : 
     201          669 :     return std::nullopt;
     202              : }
     203              : 
     204         4082 : std::optional<ClusterId> AttributePathExpandIterator::NextClusterId()
     205              : {
     206         4082 :     if (mPosition.mOutputPath.mClusterId == kInvalidClusterId)
     207              :     {
     208              :         // Cluster ID is tied to cluster index. If no cluster id available yet
     209              :         // this means index is invalid. Processing logic in output advance only resets
     210              :         // cluster ID to invalid when resetting iteration.
     211         2001 :         mClusterIndex = kInvalidIndex;
     212              :     }
     213              : 
     214         4082 :     if (mClusterIndex == kInvalidIndex)
     215              :     {
     216              :         // start a new iteration on the current endpoint
     217         3868 :         mClusters = mDataModelProvider->ServerClustersIgnoreError(mPosition.mOutputPath.mEndpointId);
     218              : 
     219         3868 :         if (mPosition.mOutputPath.mClusterId != kInvalidClusterId)
     220              :         {
     221              :             // Position on the correct cluster if we have a start point
     222         1867 :             mClusterIndex = 0;
     223         2130 :             while ((mClusterIndex < mClusters.size()) && (mClusters[mClusterIndex].clusterId != mPosition.mOutputPath.mClusterId))
     224              :             {
     225          263 :                 mClusterIndex++;
     226              :             }
     227              :         }
     228              :     }
     229              : 
     230         4082 :     if (mPosition.mOutputPath.mClusterId == kInvalidClusterId)
     231              :     {
     232              : 
     233         2001 :         if (!mPosition.mAttributePath->mValue.HasWildcardClusterId())
     234              :         {
     235              :             // The clusterID is NOT a wildcard (i.e. is fixed).
     236              :             //
     237              :             // For wildcard expansion, we validate that this is a valid cluster for the endpoint.
     238              :             // If non-wildcard expansion, we return as-is.
     239         1834 :             if (mPosition.mAttributePath->mValue.IsWildcardPath())
     240              :             {
     241          269 :                 const ClusterId clusterId = mPosition.mAttributePath->mValue.mClusterId;
     242              : 
     243          269 :                 bool found = false;
     244          399 :                 for (auto & entry : mClusters)
     245              :                 {
     246          371 :                     if (entry.clusterId == clusterId)
     247              :                     {
     248          241 :                         found = true;
     249          241 :                         break;
     250              :                     }
     251              :                 }
     252              : 
     253          269 :                 if (!found)
     254              :                 {
     255           28 :                     return std::nullopt;
     256              :                 }
     257              :             }
     258              : 
     259         1806 :             return mPosition.mAttributePath->mValue.mClusterId;
     260              :         }
     261          167 :         mClusterIndex = 0;
     262              :     }
     263              :     else
     264              :     {
     265         2081 :         mClusterIndex++;
     266              :     }
     267              : 
     268         2248 :     VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardClusterId(), std::nullopt);
     269          454 :     VerifyOrReturnValue(mClusterIndex < mClusters.size(), std::nullopt);
     270              : 
     271          280 :     return mClusters[mClusterIndex].clusterId;
     272              : }
     273              : 
     274         3902 : std::optional<EndpointId> AttributePathExpandIterator::NextEndpointId()
     275              : {
     276         3902 :     if (mEndpointIndex == kInvalidIndex)
     277              :     {
     278              :         // index is missing, have to start a new iteration
     279         2940 :         mEndpoints = mDataModelProvider->EndpointsIgnoreError();
     280              : 
     281         2940 :         if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId)
     282              :         {
     283              :             // Position on the correct endpoint if we have a start point
     284         1509 :             mEndpointIndex = 0;
     285         3127 :             while ((mEndpointIndex < mEndpoints.size()) && (mEndpoints[mEndpointIndex].id != mPosition.mOutputPath.mEndpointId))
     286              :             {
     287         1618 :                 mEndpointIndex++;
     288              :             }
     289              :         }
     290              :     }
     291              : 
     292         3902 :     if (mPosition.mOutputPath.mEndpointId == kInvalidEndpointId)
     293              :     {
     294         1906 :         if (!mPosition.mAttributePath->mValue.HasWildcardEndpointId())
     295              :         {
     296         1856 :             return mPosition.mAttributePath->mValue.mEndpointId;
     297              :         }
     298              : 
     299              :         // start from the beginning
     300           50 :         mEndpointIndex = 0;
     301              :     }
     302              :     else
     303              :     {
     304         1996 :         mEndpointIndex++;
     305              :     }
     306              : 
     307         2046 :     VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardEndpointId(), std::nullopt);
     308          202 :     VerifyOrReturnValue(mEndpointIndex < mEndpoints.size(), std::nullopt);
     309              : 
     310          145 :     return mEndpoints[mEndpointIndex].id;
     311              : }
     312              : 
     313              : } // namespace app
     314              : } // namespace chip
        

Generated by: LCOV version 2.0-1