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 2491 : AttributePathExpandIterator::AttributePathExpandIterator(DataModel::Provider * dataModel, Position & position) :
34 2491 : mDataModelProvider(dataModel), mPosition(position)
35 2491 : {}
36 :
37 11366 : bool AttributePathExpandIterator::AdvanceOutputPath()
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 11366 : if (mPosition.mOutputPath.mClusterId != kInvalidClusterId)
56 : {
57 7465 : std::optional<AttributeId> nextAttribute = NextAttributeId();
58 7465 : if (nextAttribute.has_value())
59 : {
60 5387 : mPosition.mOutputPath.mAttributeId = *nextAttribute;
61 5387 : mPosition.mOutputPath.mExpanded = mPosition.mAttributePath->mValue.IsWildcardPath();
62 5387 : return true;
63 : }
64 : }
65 :
66 : // no valid attribute, try to advance the cluster, see if a suitable one exists
67 5979 : if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId)
68 : {
69 4076 : std::optional<ClusterId> nextCluster = NextClusterId();
70 4076 : 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 2083 : mPosition.mOutputPath.mClusterId = *nextCluster;
76 2083 : mPosition.mOutputPath.mAttributeId = kInvalidAttributeId;
77 2083 : continue;
78 : }
79 : }
80 :
81 : // No valid cluster, try advance the endpoint, see if a suitable one exists.
82 3896 : std::optional<EndpointId> nextEndpoint = NextEndpointId();
83 3896 : 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 1998 : mPosition.mOutputPath.mEndpointId = *nextEndpoint;
89 1998 : mPosition.mOutputPath.mClusterId = kInvalidClusterId;
90 1998 : continue;
91 : }
92 1898 : return false;
93 4081 : }
94 : }
95 :
96 7045 : bool AttributePathExpandIterator::Next(ConcreteAttributePath & path)
97 : {
98 8943 : while (mPosition.mAttributePath != nullptr)
99 : {
100 7285 : if (AdvanceOutputPath())
101 : {
102 5387 : path = mPosition.mOutputPath;
103 5387 : return true;
104 : }
105 1898 : mPosition.mAttributePath = mPosition.mAttributePath->mpNext;
106 1898 : mPosition.mOutputPath = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId);
107 : }
108 :
109 1658 : return false;
110 : }
111 :
112 48 : bool AttributePathExpandIterator::IsValidAttributeId(AttributeId attributeId)
113 : {
114 48 : switch (attributeId)
115 : {
116 4 : case Clusters::Globals::Attributes::GeneratedCommandList::Id:
117 : case Clusters::Globals::Attributes::AcceptedCommandList::Id:
118 : case Clusters::Globals::Attributes::AttributeList::Id:
119 4 : return true;
120 44 : default:
121 44 : break;
122 : }
123 :
124 44 : DataModel::AttributeFinder finder(mDataModelProvider);
125 :
126 44 : const ConcreteAttributePath attributePath(mPosition.mOutputPath.mEndpointId, mPosition.mOutputPath.mClusterId, attributeId);
127 44 : return finder.Find(attributePath).has_value();
128 : }
129 :
130 7465 : std::optional<AttributeId> AttributePathExpandIterator::NextAttributeId()
131 : {
132 7465 : if (mPosition.mOutputPath.mAttributeId == kInvalidAttributeId)
133 : {
134 : // Attribute ID is tied to attribute index. If no attribute id is available yet
135 : // this means the index is invalid. Processing logic in output advance only resets
136 : // attribute ID to invalid when resetting iteration.
137 2086 : mAttributeIndex = kInvalidIndex;
138 : }
139 :
140 7465 : if (mAttributeIndex == kInvalidIndex)
141 : {
142 : // start a new iteration of attributes on the current cluster path.
143 3946 : mAttributes = mDataModelProvider->AttributesIgnoreError(mPosition.mOutputPath);
144 :
145 3946 : if (mPosition.mOutputPath.mAttributeId != kInvalidAttributeId)
146 : {
147 : // Position on the correct attribute if we have a start point
148 1860 : mAttributeIndex = 0;
149 14168 : while ((mAttributeIndex < mAttributes.size()) &&
150 7082 : (mAttributes[mAttributeIndex].attributeId != mPosition.mOutputPath.mAttributeId))
151 : {
152 5226 : mAttributeIndex++;
153 : }
154 : }
155 : }
156 :
157 7465 : if (mPosition.mOutputPath.mAttributeId == kInvalidAttributeId)
158 : {
159 2086 : if (!mPosition.mAttributePath->mValue.HasWildcardAttributeId())
160 : {
161 : // The attributeID is NOT a wildcard (i.e. it is fixed).
162 : //
163 : // For wildcard expansion, we validate that this is a valid attribute for the given
164 : // cluster on the given endpoint. If not a wildcard expansion, return it as-is.
165 1610 : if (mPosition.mAttributePath->mValue.IsWildcardPath())
166 : {
167 48 : if (!IsValidAttributeId(mPosition.mAttributePath->mValue.mAttributeId))
168 : {
169 20 : return std::nullopt;
170 : }
171 : }
172 1590 : return mPosition.mAttributePath->mValue.mAttributeId;
173 : }
174 476 : mAttributeIndex = 0;
175 : }
176 : else
177 : {
178 5379 : mAttributeIndex++;
179 : }
180 :
181 : // Advance the existing attribute id if it can be advanced.
182 5855 : VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardAttributeId(), std::nullopt);
183 :
184 4466 : if (mAttributeIndex < mAttributes.size())
185 : {
186 3797 : return mAttributes[mAttributeIndex].attributeId;
187 : }
188 :
189 669 : return std::nullopt;
190 : }
191 :
192 4076 : std::optional<ClusterId> AttributePathExpandIterator::NextClusterId()
193 : {
194 4076 : if (mPosition.mOutputPath.mClusterId == kInvalidClusterId)
195 : {
196 : // Cluster ID is tied to cluster index. If no cluster id available yet
197 : // this means index is invalid. Processing logic in output advance only resets
198 : // cluster ID to invalid when resetting iteration.
199 1998 : mClusterIndex = kInvalidIndex;
200 : }
201 :
202 4076 : if (mClusterIndex == kInvalidIndex)
203 : {
204 : // start a new iteration on the current endpoint
205 3862 : mClusters = mDataModelProvider->ServerClustersIgnoreError(mPosition.mOutputPath.mEndpointId);
206 :
207 3862 : if (mPosition.mOutputPath.mClusterId != kInvalidClusterId)
208 : {
209 : // Position on the correct cluster if we have a start point
210 1864 : mClusterIndex = 0;
211 2127 : while ((mClusterIndex < mClusters.size()) && (mClusters[mClusterIndex].clusterId != mPosition.mOutputPath.mClusterId))
212 : {
213 263 : mClusterIndex++;
214 : }
215 : }
216 : }
217 :
218 4076 : if (mPosition.mOutputPath.mClusterId == kInvalidClusterId)
219 : {
220 :
221 1998 : if (!mPosition.mAttributePath->mValue.HasWildcardClusterId())
222 : {
223 : // The clusterID is NOT a wildcard (i.e. is fixed).
224 : //
225 : // For wildcard expansion, we validate that this is a valid cluster for the endpoint.
226 : // If non-wildcard expansion, we return as-is.
227 1831 : if (mPosition.mAttributePath->mValue.IsWildcardPath())
228 : {
229 269 : const ClusterId clusterId = mPosition.mAttributePath->mValue.mClusterId;
230 :
231 269 : bool found = false;
232 399 : for (auto & entry : mClusters)
233 : {
234 371 : if (entry.clusterId == clusterId)
235 : {
236 241 : found = true;
237 241 : break;
238 : }
239 : }
240 :
241 269 : if (!found)
242 : {
243 28 : return std::nullopt;
244 : }
245 : }
246 :
247 1803 : return mPosition.mAttributePath->mValue.mClusterId;
248 : }
249 167 : mClusterIndex = 0;
250 : }
251 : else
252 : {
253 2078 : mClusterIndex++;
254 : }
255 :
256 2245 : VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardClusterId(), std::nullopt);
257 454 : VerifyOrReturnValue(mClusterIndex < mClusters.size(), std::nullopt);
258 :
259 280 : return mClusters[mClusterIndex].clusterId;
260 : }
261 :
262 3896 : std::optional<EndpointId> AttributePathExpandIterator::NextEndpointId()
263 : {
264 3896 : if (mEndpointIndex == kInvalidIndex)
265 : {
266 : // index is missing, have to start a new iteration
267 2934 : mEndpoints = mDataModelProvider->EndpointsIgnoreError();
268 :
269 2934 : if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId)
270 : {
271 : // Position on the correct endpoint if we have a start point
272 1506 : mEndpointIndex = 0;
273 3121 : while ((mEndpointIndex < mEndpoints.size()) && (mEndpoints[mEndpointIndex].id != mPosition.mOutputPath.mEndpointId))
274 : {
275 1615 : mEndpointIndex++;
276 : }
277 : }
278 : }
279 :
280 3896 : if (mPosition.mOutputPath.mEndpointId == kInvalidEndpointId)
281 : {
282 1903 : if (!mPosition.mAttributePath->mValue.HasWildcardEndpointId())
283 : {
284 1853 : return mPosition.mAttributePath->mValue.mEndpointId;
285 : }
286 :
287 : // start from the beginning
288 50 : mEndpointIndex = 0;
289 : }
290 : else
291 : {
292 1993 : mEndpointIndex++;
293 : }
294 :
295 2043 : VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardEndpointId(), std::nullopt);
296 202 : VerifyOrReturnValue(mEndpointIndex < mEndpoints.size(), std::nullopt);
297 :
298 145 : return mEndpoints[mEndpointIndex].id;
299 : }
300 :
301 : } // namespace app
302 : } // namespace chip
|