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 2496 : AttributePathExpandIterator::AttributePathExpandIterator(DataModel::Provider * dataModel, Position & position) :
34 2496 : mDataModelProvider(dataModel), mPosition(position)
35 2496 : {}
36 :
37 11956 : 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 11956 : if (mPosition.mOutputPath.mClusterId != kInvalidClusterId)
56 : {
57 8045 : std::optional<AttributeId> nextAttribute = NextAttribute(entry);
58 8045 : if (nextAttribute.has_value())
59 : {
60 5889 : mPosition.mOutputPath.mAttributeId = *nextAttribute;
61 5889 : mPosition.mOutputPath.mExpanded = mPosition.mAttributePath->mValue.IsWildcardPath();
62 5889 : return true;
63 : }
64 : }
65 :
66 : // no valid attribute, try to advance the cluster, see if a suitable one exists
67 6067 : if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId)
68 : {
69 4160 : std::optional<ClusterId> nextCluster = NextClusterId();
70 4160 : 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 2161 : mPosition.mOutputPath.mClusterId = *nextCluster;
76 2161 : mPosition.mOutputPath.mAttributeId = kInvalidAttributeId;
77 2161 : continue;
78 : }
79 : }
80 :
81 : // No valid cluster, try advance the endpoint, see if a suitable one exists.
82 3906 : std::optional<EndpointId> nextEndpoint = NextEndpointId();
83 3906 : 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 2004 : mPosition.mOutputPath.mEndpointId = *nextEndpoint;
89 2004 : mPosition.mOutputPath.mClusterId = kInvalidClusterId;
90 2004 : continue;
91 : }
92 1902 : return false;
93 4165 : }
94 : }
95 :
96 7551 : bool AttributePathExpandIterator::Next(ConcreteAttributePath & path, std::optional<DataModel::AttributeEntry> * entry)
97 : {
98 9453 : while (mPosition.mAttributePath != nullptr)
99 : {
100 7791 : if (AdvanceOutputPath(entry))
101 : {
102 5889 : path = mPosition.mOutputPath;
103 5889 : return true;
104 : }
105 1902 : mPosition.mAttributePath = mPosition.mAttributePath->mpNext;
106 1902 : mPosition.mOutputPath = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId);
107 : }
108 :
109 1662 : return false;
110 : }
111 :
112 8045 : std::optional<AttributeId> AttributePathExpandIterator::NextAttribute(std::optional<DataModel::AttributeEntry> * entry)
113 : {
114 8045 : 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 2164 : mAttributeIndex = kInvalidIndex;
120 : }
121 :
122 8045 : if (mAttributeIndex == kInvalidIndex)
123 : {
124 : // start a new iteration of attributes on the current cluster path.
125 4029 : mAttributes = mDataModelProvider->AttributesIgnoreError(mPosition.mOutputPath);
126 :
127 4029 : if (mPosition.mOutputPath.mAttributeId != kInvalidAttributeId)
128 : {
129 : // Position on the correct attribute if we have a start point
130 1865 : mAttributeIndex = 0;
131 14199 : while ((mAttributeIndex < mAttributes.size()) &&
132 7097 : (mAttributes[mAttributeIndex].attributeId != mPosition.mOutputPath.mAttributeId))
133 : {
134 5237 : mAttributeIndex++;
135 : }
136 : }
137 : }
138 :
139 8045 : if (mPosition.mOutputPath.mAttributeId == kInvalidAttributeId)
140 : {
141 2164 : 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 1615 : DataModel::AttributeFinder finder(mDataModelProvider);
148 :
149 1615 : const ConcreteAttributePath attributePath(mPosition.mOutputPath.mEndpointId, mPosition.mOutputPath.mClusterId,
150 1615 : mPosition.mAttributePath->mValue.mAttributeId);
151 1615 : std::optional<DataModel::AttributeEntry> foundEntry = finder.Find(attributePath);
152 :
153 : // if the entry is valid, we can just return it
154 1615 : if (foundEntry.has_value())
155 : {
156 1589 : if (entry)
157 : {
158 2 : entry->emplace(*foundEntry);
159 : }
160 1589 : 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 26 : if (mPosition.mAttributePath->mValue.IsWildcardPath())
166 : {
167 21 : 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 5 : if (entry)
176 : {
177 0 : entry->reset();
178 : }
179 : // forced ID (even if invalid)
180 5 : return mPosition.mAttributePath->mValue.mAttributeId;
181 1615 : }
182 549 : mAttributeIndex = 0;
183 : }
184 : else
185 : {
186 5881 : mAttributeIndex++;
187 : }
188 :
189 : // Advance the existing attribute id if it can be advanced.
190 6430 : VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardAttributeId(), std::nullopt);
191 :
192 5037 : if (mAttributeIndex < mAttributes.size())
193 : {
194 4295 : if (entry != nullptr)
195 : {
196 6 : entry->emplace(mAttributes[mAttributeIndex]);
197 : }
198 4295 : return mAttributes[mAttributeIndex].attributeId;
199 : }
200 :
201 742 : return std::nullopt;
202 : }
203 :
204 4160 : std::optional<ClusterId> AttributePathExpandIterator::NextClusterId()
205 : {
206 4160 : 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 2004 : mClusterIndex = kInvalidIndex;
212 : }
213 :
214 4160 : if (mClusterIndex == kInvalidIndex)
215 : {
216 : // start a new iteration on the current endpoint
217 3875 : mClusters = mDataModelProvider->ServerClustersIgnoreError(mPosition.mOutputPath.mEndpointId);
218 :
219 3875 : if (mPosition.mOutputPath.mClusterId != kInvalidClusterId)
220 : {
221 : // Position on the correct cluster if we have a start point
222 1871 : mClusterIndex = 0;
223 2140 : while ((mClusterIndex < mClusters.size()) && (mClusters[mClusterIndex].clusterId != mPosition.mOutputPath.mClusterId))
224 : {
225 269 : mClusterIndex++;
226 : }
227 : }
228 : }
229 :
230 4160 : if (mPosition.mOutputPath.mClusterId == kInvalidClusterId)
231 : {
232 :
233 2004 : 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 1837 : if (mPosition.mAttributePath->mValue.IsWildcardPath())
240 : {
241 271 : const ClusterId clusterId = mPosition.mAttributePath->mValue.mClusterId;
242 :
243 271 : bool found = false;
244 404 : for (auto & entry : mClusters)
245 : {
246 375 : if (entry.clusterId == clusterId)
247 : {
248 242 : found = true;
249 242 : break;
250 : }
251 : }
252 :
253 271 : if (!found)
254 : {
255 29 : return std::nullopt;
256 : }
257 : }
258 :
259 1808 : return mPosition.mAttributePath->mValue.mClusterId;
260 : }
261 167 : mClusterIndex = 0;
262 : }
263 : else
264 : {
265 2156 : mClusterIndex++;
266 : }
267 :
268 2323 : VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardClusterId(), std::nullopt);
269 527 : VerifyOrReturnValue(mClusterIndex < mClusters.size(), std::nullopt);
270 :
271 353 : return mClusters[mClusterIndex].clusterId;
272 : }
273 :
274 3906 : std::optional<EndpointId> AttributePathExpandIterator::NextEndpointId()
275 : {
276 3906 : if (mEndpointIndex == kInvalidIndex)
277 : {
278 : // index is missing, have to start a new iteration
279 2942 : mEndpoints = mDataModelProvider->EndpointsIgnoreError();
280 :
281 2942 : if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId)
282 : {
283 : // Position on the correct endpoint if we have a start point
284 1510 : mEndpointIndex = 0;
285 3128 : while ((mEndpointIndex < mEndpoints.size()) && (mEndpoints[mEndpointIndex].id != mPosition.mOutputPath.mEndpointId))
286 : {
287 1618 : mEndpointIndex++;
288 : }
289 : }
290 : }
291 :
292 3906 : if (mPosition.mOutputPath.mEndpointId == kInvalidEndpointId)
293 : {
294 1907 : if (!mPosition.mAttributePath->mValue.HasWildcardEndpointId())
295 : {
296 1857 : return mPosition.mAttributePath->mValue.mEndpointId;
297 : }
298 :
299 : // start from the beginning
300 50 : mEndpointIndex = 0;
301 : }
302 : else
303 : {
304 1999 : mEndpointIndex++;
305 : }
306 :
307 2049 : VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardEndpointId(), std::nullopt);
308 204 : VerifyOrReturnValue(mEndpointIndex < mEndpoints.size(), std::nullopt);
309 :
310 147 : return mEndpoints[mEndpointIndex].id;
311 : }
312 :
313 : } // namespace app
314 : } // namespace chip
|