Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 : #pragma once
19 :
20 : #include <app/AttributePathParams.h>
21 : #include <app/ConcreteAttributePath.h>
22 : #include <app/data-model-provider/MetadataTypes.h>
23 : #include <app/data-model-provider/Provider.h>
24 : #include <lib/core/DataModelTypes.h>
25 : #include <lib/support/LinkedList.h>
26 : #include <lib/support/ReadOnlyBuffer.h>
27 : #include <lib/support/Span.h>
28 :
29 : #include <limits>
30 :
31 : namespace chip {
32 : namespace app {
33 :
34 : /// Handles attribute path expansions
35 : /// Usage:
36 : ///
37 : /// - Start iterating by creating an iteration state
38 : ///
39 : /// AttributePathExpandIterator::Position position = AttributePathExpandIterator::Position::StartIterating(path);
40 : ///
41 : /// - Use the iteration state in a for loop:
42 : ///
43 : /// ConcreteAttributePath path;
44 : /// for (AttributePathExpandIterator iterator(position); iterator->Next(path);) {
45 : /// // use `path` here`
46 : /// }
47 : ///
48 : /// OR:
49 : ///
50 : /// ConcreteAttributePath path;
51 : /// AttributePathExpandIterator iterator(position);
52 : ///
53 : /// while (iterator.Next(path)) {
54 : /// // use `path` here`
55 : /// }
56 : ///
57 : /// Usage requirements and assumptions:
58 : ///
59 : /// - An ` AttributePathExpandIterator::Position` can only be used by a single AttributePathExpandIterator at a time.
60 : ///
61 : /// - `position` is automatically updated by the AttributePathExpandIterator, so
62 : /// calling `Next` on the iterator will update the position cursor variable.
63 : ///
64 : class AttributePathExpandIterator
65 : {
66 : public:
67 : class Position
68 : {
69 : public:
70 : // Position is treated as a direct member access by the AttributePathExpandIterator, however it is opaque (except copying)
71 : // for external code. We allow friendship here to not have specific get/set for methods (clearer interface and less
72 : // likelihood of extra code usage).
73 : friend class AttributePathExpandIterator;
74 :
75 : /// External callers can only ever start iterating on a new path from the beginning
76 2146 : static Position StartIterating(SingleLinkedListNode<AttributePathParams> * path) { return Position(path); }
77 :
78 : /// Copies are allowed
79 : Position(const Position &) = default;
80 : Position & operator=(const Position &) = default;
81 :
82 1132 : Position() : mAttributePath(nullptr) {}
83 :
84 : /// Reset the iterator to the beginning of current cluster if we are in the middle of expanding a wildcard attribute id for
85 : /// some cluster.
86 : ///
87 : /// When attributes are changed in the middle of expanding a wildcard attribute, we need to reset the iterator, to provide
88 : /// the client with a consistent state of the cluster.
89 264 : void IterateFromTheStartOfTheCurrentClusterIfAttributeWildcard()
90 : {
91 264 : VerifyOrReturn(mAttributePath != nullptr && mAttributePath->mValue.HasWildcardAttributeId());
92 4 : mOutputPath.mAttributeId = kInvalidAttributeId;
93 : }
94 :
95 : protected:
96 2146 : Position(SingleLinkedListNode<AttributePathParams> * path) :
97 2146 : mAttributePath(path), mOutputPath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)
98 2146 : {}
99 :
100 : SingleLinkedListNode<AttributePathParams> * mAttributePath;
101 : ConcreteAttributePath mOutputPath;
102 : };
103 :
104 : AttributePathExpandIterator(DataModel::Provider * dataModel, Position & position);
105 :
106 : // This class may not be copied. A new one should be created when needed and they
107 : // should not overlap.
108 : AttributePathExpandIterator(const AttributePathExpandIterator &) = delete;
109 : AttributePathExpandIterator & operator=(const AttributePathExpandIterator &) = delete;
110 :
111 : /// Get the next path of the expansion (if one exists).
112 : ///
113 : /// Can also optionally ask for the corresponding AttributeEntry (e.g. to validate
114 : /// read/write options).
115 : ///
116 : /// @param entry - an optional out argument for the corresponding attribute entry metadata
117 : /// for the given path. Since the expand iterator looks over cluster metadata
118 : /// to generate valid paths, the metadata information is `free` to receive
119 : /// by the caller.
120 : ///
121 : /// NOTES:
122 : /// - returning the `entry` information is done here as a convenience/optimization
123 : /// to avoid extra lookups for metadata. Callers are free to use `expanded path` instead
124 : /// and not ask for the entry data.
125 : /// - `entry` may be `std::nullopt`: AttributePathExpandIterator will return non-wildcard
126 : /// paths as-is and those may be invalid. If a path is not valid for the DataModel::Provider
127 : /// then entry will be `std::nullopt`.
128 : ///
129 : /// On success, true is returned and `path` is filled with the next path in the
130 : /// expansion.
131 : /// On iteration completion, false is returned and the content of path IS NOT DEFINED.
132 : bool Next(ConcreteAttributePath & path, std::optional<DataModel::AttributeEntry> * entry = nullptr);
133 :
134 : private:
135 : static constexpr size_t kInvalidIndex = std::numeric_limits<size_t>::max();
136 :
137 : DataModel::Provider * mDataModelProvider;
138 : Position & mPosition;
139 :
140 : ReadOnlyBuffer<DataModel::EndpointEntry> mEndpoints; // all endpoints
141 : size_t mEndpointIndex = kInvalidIndex;
142 :
143 : ReadOnlyBuffer<DataModel::ServerClusterEntry> mClusters; // all clusters ON THE CURRENT endpoint
144 : size_t mClusterIndex = kInvalidIndex;
145 :
146 : ReadOnlyBuffer<DataModel::AttributeEntry> mAttributes; // all attributes ON THE CURRENT cluster
147 : size_t mAttributeIndex = kInvalidIndex;
148 :
149 : /// Move to the next endpoint/cluster/attribute triplet that is valid given
150 : /// the current mOutputPath and mpAttributePath.
151 : ///
152 : /// returns true if such a next value was found.
153 : bool AdvanceOutputPath(std::optional<DataModel::AttributeEntry> * entry);
154 :
155 : /// Get the next attribute ID in mOutputPath(endpoint/cluster) if one is available.
156 : /// Will start from the beginning if current mOutputPath.mAttributeId is kInvalidAttributeId
157 : ///
158 : /// Respects path expansion/values in mpAttributePath
159 : std::optional<AttributeId> NextAttribute(std::optional<DataModel::AttributeEntry> * entry);
160 :
161 : /// Get the next cluster ID in mOutputPath(endpoint) if one is available.
162 : /// Will start from the beginning if current mOutputPath.mClusterId is kInvalidClusterId
163 : ///
164 : /// Respects path expansion/values in mpAttributePath
165 : std::optional<ClusterId> NextClusterId();
166 :
167 : /// Get the next endpoint ID in mOutputPath if one is available.
168 : /// Will start from the beginning if current mOutputPath.mEndpointId is kInvalidEndpointId
169 : ///
170 : /// Respects path expansion/values in mpAttributePath
171 : std::optional<EndpointId> NextEndpointId();
172 : };
173 :
174 : /// RollbackAttributePathExpandIterator is an AttributePathExpandIterator wrapper that rolls back the Next()
175 : /// call whenever a new `MarkCompleted()` method is not called.
176 : ///
177 : /// Example use cases:
178 : ///
179 : /// - Iterate over all attributes and process one-by-one, however when the iteration fails, resume at
180 : /// the last failure point:
181 : ///
182 : /// RollbackAttributePathExpandIterator iterator(....);
183 : /// ConcreteAttributePath path;
184 : ///
185 : /// for ( ; iterator.Next(path); iterator.MarkCompleted()) {
186 : /// if (!CanProcess(path)) {
187 : /// // iterator state IS PRESERVED so that Next() will return the SAME path on the next call.
188 : /// return CHIP_ERROR_TRY_AGAIN_LATER;
189 : /// }
190 : /// }
191 : ///
192 : /// - Grab what the next output path would be WITHOUT advancing a state;
193 : ///
194 : /// {
195 : /// RollbackAttributePathExpandIterator iterator(...., state);
196 : /// if (iterator.Next(...)) { ... }
197 : /// }
198 : /// // state here is ROLLED BACK (i.e. initializing a new iterator with it will start at the same place as the previous
199 : /// iteration attempt).
200 : ///
201 : ///
202 : class RollbackAttributePathExpandIterator
203 : {
204 : public:
205 1980 : RollbackAttributePathExpandIterator(DataModel::Provider * dataModel, AttributePathExpandIterator::Position & position) :
206 1980 : mAttributePathExpandIterator(dataModel, position), mPositionTarget(position), mCompletedPosition(position)
207 1980 : {}
208 1980 : ~RollbackAttributePathExpandIterator() { mPositionTarget = mCompletedPosition; }
209 :
210 6463 : bool Next(ConcreteAttributePath & path) { return mAttributePathExpandIterator.Next(path); }
211 :
212 : /// Marks the current iteration completed (so peek does not actually roll back)
213 4483 : void MarkCompleted() { mCompletedPosition = mPositionTarget; }
214 :
215 : private:
216 : AttributePathExpandIterator mAttributePathExpandIterator;
217 : AttributePathExpandIterator::Position & mPositionTarget;
218 : AttributePathExpandIterator::Position mCompletedPosition;
219 : };
220 :
221 : } // namespace app
222 : } // namespace chip
|