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/MetadataList.h>
21 : #include <app/data-model-provider/MetadataLookup.h>
22 : #include <app/data-model-provider/MetadataTypes.h>
23 : #include <lib/core/DataModelTypes.h>
24 : #include <lib/support/CodeUtils.h>
25 :
26 : #include <optional>
27 :
28 : using namespace chip::app::DataModel;
29 :
30 : namespace chip {
31 : namespace app {
32 :
33 2484 : AttributePathExpandIterator::AttributePathExpandIterator(DataModel::Provider * dataModel, Position & position) :
34 2484 : mDataModelProvider(dataModel), mPosition(position)
35 2484 : {}
36 :
37 11338 : 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 11338 : if (mPosition.mOutputPath.mClusterId != kInvalidClusterId)
56 : {
57 7451 : std::optional<AttributeId> nextAttribute = NextAttributeId();
58 7451 : if (nextAttribute.has_value())
59 : {
60 5380 : mPosition.mOutputPath.mAttributeId = *nextAttribute;
61 5380 : mPosition.mOutputPath.mExpanded = mPosition.mAttributePath->mValue.IsWildcardPath();
62 5380 : return true;
63 : }
64 : }
65 :
66 : // no valid attribute, try to advance the cluster, see if a suitable one exists
67 5958 : if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId)
68 : {
69 4062 : std::optional<ClusterId> nextCluster = NextClusterId();
70 4062 : 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 2076 : mPosition.mOutputPath.mClusterId = *nextCluster;
76 2076 : mPosition.mOutputPath.mAttributeId = kInvalidAttributeId;
77 2076 : continue;
78 : }
79 : }
80 :
81 : // No valid cluster, try advance the endpoint, see if a suitable one exists.
82 3882 : std::optional<EndpointId> nextEndpoint = NextEndpointId();
83 3882 : 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 1991 : mPosition.mOutputPath.mEndpointId = *nextEndpoint;
89 1991 : mPosition.mOutputPath.mClusterId = kInvalidClusterId;
90 1991 : continue;
91 : }
92 1891 : return false;
93 4067 : }
94 : }
95 :
96 7031 : bool AttributePathExpandIterator::Next(ConcreteAttributePath & path)
97 : {
98 8922 : while (mPosition.mAttributePath != nullptr)
99 : {
100 7271 : if (AdvanceOutputPath())
101 : {
102 5380 : path = mPosition.mOutputPath;
103 5380 : return true;
104 : }
105 1891 : mPosition.mAttributePath = mPosition.mAttributePath->mpNext;
106 1891 : mPosition.mOutputPath = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId);
107 : }
108 :
109 1651 : 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 7451 : std::optional<AttributeId> AttributePathExpandIterator::NextAttributeId()
131 : {
132 7451 : 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 2079 : mAttributeIndex = kInvalidIndex;
138 : }
139 :
140 7451 : if (mAttributeIndex == kInvalidIndex)
141 : {
142 : // start a new iteration of attributes on the current cluster path.
143 3932 : mAttributes = mDataModelProvider->AttributesIgnoreError(mPosition.mOutputPath);
144 :
145 3932 : if (mPosition.mOutputPath.mAttributeId != kInvalidAttributeId)
146 : {
147 : // Position on the correct attribute if we have a start point
148 1853 : mAttributeIndex = 0;
149 14126 : while ((mAttributeIndex < mAttributes.size()) &&
150 7061 : (mAttributes[mAttributeIndex].attributeId != mPosition.mOutputPath.mAttributeId))
151 : {
152 5212 : mAttributeIndex++;
153 : }
154 : }
155 : }
156 :
157 7451 : if (mPosition.mOutputPath.mAttributeId == kInvalidAttributeId)
158 : {
159 2079 : 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 1603 : if (mPosition.mAttributePath->mValue.IsWildcardPath())
166 : {
167 48 : if (!IsValidAttributeId(mPosition.mAttributePath->mValue.mAttributeId))
168 : {
169 20 : return std::nullopt;
170 : }
171 : }
172 1583 : return mPosition.mAttributePath->mValue.mAttributeId;
173 : }
174 476 : mAttributeIndex = 0;
175 : }
176 : else
177 : {
178 5372 : mAttributeIndex++;
179 : }
180 :
181 : // Advance the existing attribute id if it can be advanced.
182 5848 : VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardAttributeId(), std::nullopt);
183 :
184 : // Ensure (including ordering) that GlobalAttributesNotInMetadata is reported as needed
185 14691 : for (unsigned i = 0; i < MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata); i++)
186 : {
187 11907 : if (GlobalAttributesNotInMetadata[i] != mPosition.mOutputPath.mAttributeId)
188 : {
189 10225 : continue;
190 : }
191 :
192 1682 : unsigned nextAttributeIndex = i + 1;
193 1682 : if (nextAttributeIndex < MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata))
194 : {
195 1013 : return GlobalAttributesNotInMetadata[nextAttributeIndex];
196 : }
197 :
198 : // Reached the end of global attributes. Since global attributes are
199 : // reported last, finishing global attributes means everything completed.
200 669 : return std::nullopt;
201 : }
202 :
203 2784 : if (mAttributeIndex < mAttributes.size())
204 : {
205 2784 : return mAttributes[mAttributeIndex].attributeId;
206 : }
207 :
208 : // Finished the data model, start with global attributes
209 : static_assert(MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata) > 0);
210 0 : return GlobalAttributesNotInMetadata[0];
211 : }
212 :
213 4062 : std::optional<ClusterId> AttributePathExpandIterator::NextClusterId()
214 : {
215 4062 : if (mPosition.mOutputPath.mClusterId == kInvalidClusterId)
216 : {
217 : // Cluster ID is tied to cluster index. If no cluster id available yet
218 : // this means index is invalid. Processing logic in output advance only resets
219 : // cluster ID to invalid when resetting iteration.
220 1991 : mClusterIndex = kInvalidIndex;
221 : }
222 :
223 4062 : if (mClusterIndex == kInvalidIndex)
224 : {
225 : // start a new iteration on the current endpoint
226 3848 : mClusters = mDataModelProvider->ServerClustersIgnoreError(mPosition.mOutputPath.mEndpointId);
227 :
228 3848 : if (mPosition.mOutputPath.mClusterId != kInvalidClusterId)
229 : {
230 : // Position on the correct cluster if we have a start point
231 1857 : mClusterIndex = 0;
232 2120 : while ((mClusterIndex < mClusters.size()) && (mClusters[mClusterIndex].clusterId != mPosition.mOutputPath.mClusterId))
233 : {
234 263 : mClusterIndex++;
235 : }
236 : }
237 : }
238 :
239 4062 : if (mPosition.mOutputPath.mClusterId == kInvalidClusterId)
240 : {
241 :
242 1991 : if (!mPosition.mAttributePath->mValue.HasWildcardClusterId())
243 : {
244 : // The clusterID is NOT a wildcard (i.e. is fixed).
245 : //
246 : // For wildcard expansion, we validate that this is a valid cluster for the endpoint.
247 : // If non-wildcard expansion, we return as-is.
248 1824 : if (mPosition.mAttributePath->mValue.IsWildcardPath())
249 : {
250 269 : const ClusterId clusterId = mPosition.mAttributePath->mValue.mClusterId;
251 :
252 269 : bool found = false;
253 399 : for (auto & entry : mClusters)
254 : {
255 371 : if (entry.clusterId == clusterId)
256 : {
257 241 : found = true;
258 241 : break;
259 : }
260 : }
261 :
262 269 : if (!found)
263 : {
264 28 : return std::nullopt;
265 : }
266 : }
267 :
268 1796 : return mPosition.mAttributePath->mValue.mClusterId;
269 : }
270 167 : mClusterIndex = 0;
271 : }
272 : else
273 : {
274 2071 : mClusterIndex++;
275 : }
276 :
277 2238 : VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardClusterId(), std::nullopt);
278 454 : VerifyOrReturnValue(mClusterIndex < mClusters.size(), std::nullopt);
279 :
280 280 : return mClusters[mClusterIndex].clusterId;
281 : }
282 :
283 3882 : std::optional<EndpointId> AttributePathExpandIterator::NextEndpointId()
284 : {
285 3882 : if (mEndpointIndex == kInvalidIndex)
286 : {
287 : // index is missing, have to start a new iteration
288 2920 : mEndpoints = mDataModelProvider->EndpointsIgnoreError();
289 :
290 2920 : if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId)
291 : {
292 : // Position on the correct endpoint if we have a start point
293 1499 : mEndpointIndex = 0;
294 3107 : while ((mEndpointIndex < mEndpoints.size()) && (mEndpoints[mEndpointIndex].id != mPosition.mOutputPath.mEndpointId))
295 : {
296 1608 : mEndpointIndex++;
297 : }
298 : }
299 : }
300 :
301 3882 : if (mPosition.mOutputPath.mEndpointId == kInvalidEndpointId)
302 : {
303 1896 : if (!mPosition.mAttributePath->mValue.HasWildcardEndpointId())
304 : {
305 1846 : return mPosition.mAttributePath->mValue.mEndpointId;
306 : }
307 :
308 : // start from the beginning
309 50 : mEndpointIndex = 0;
310 : }
311 : else
312 : {
313 1986 : mEndpointIndex++;
314 : }
315 :
316 2036 : VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardEndpointId(), std::nullopt);
317 202 : VerifyOrReturnValue(mEndpointIndex < mEndpoints.size(), std::nullopt);
318 :
319 145 : return mEndpoints[mEndpointIndex].id;
320 : }
321 :
322 : } // namespace app
323 : } // namespace chip
|