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
|