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/MetadataList.h>
23 : #include <app/data-model-provider/MetadataTypes.h>
24 : #include <app/data-model-provider/Provider.h>
25 : #include <lib/core/DataModelTypes.h>
26 : #include <lib/support/LinkedList.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 2132 : 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 1125 : 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 2132 : Position(SingleLinkedListNode<AttributePathParams> * path) :
97 2132 : mAttributePath(path), mOutputPath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)
98 2132 : {}
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 : /// On success, true is returned and `path` is filled with the next path in the
114 : /// expansion.
115 : /// On iteration completion, false is returned and the content of path IS NOT DEFINED.
116 : bool Next(ConcreteAttributePath & path);
117 :
118 : private:
119 : static constexpr size_t kInvalidIndex = std::numeric_limits<size_t>::max();
120 :
121 : DataModel::Provider * mDataModelProvider;
122 : Position & mPosition;
123 :
124 : DataModel::ReadOnlyBuffer<DataModel::EndpointEntry> mEndpoints; // all endpoints
125 : size_t mEndpointIndex = kInvalidIndex;
126 :
127 : DataModel::ReadOnlyBuffer<DataModel::ServerClusterEntry> mClusters; // all clusters ON THE CURRENT endpoint
128 : size_t mClusterIndex = kInvalidIndex;
129 :
130 : DataModel::ReadOnlyBuffer<DataModel::AttributeEntry> mAttributes; // all attributes ON THE CURRENT cluster
131 : size_t mAttributeIndex = kInvalidIndex;
132 :
133 : /// Move to the next endpoint/cluster/attribute triplet that is valid given
134 : /// the current mOutputPath and mpAttributePath.
135 : ///
136 : /// returns true if such a next value was found.
137 : bool AdvanceOutputPath();
138 :
139 : /// Get the next attribute ID in mOutputPath(endpoint/cluster) if one is available.
140 : /// Will start from the beginning if current mOutputPath.mAttributeId is kInvalidAttributeId
141 : ///
142 : /// Respects path expansion/values in mpAttributePath
143 : ///
144 : /// Handles Global attributes (which are returned at the end)
145 : std::optional<AttributeId> NextAttributeId();
146 :
147 : /// Get the next cluster ID in mOutputPath(endpoint) if one is available.
148 : /// Will start from the beginning if current mOutputPath.mClusterId is kInvalidClusterId
149 : ///
150 : /// Respects path expansion/values in mpAttributePath
151 : std::optional<ClusterId> NextClusterId();
152 :
153 : /// Get the next endpoint ID in mOutputPath if one is available.
154 : /// Will start from the beginning if current mOutputPath.mEndpointId is kInvalidEndpointId
155 : ///
156 : /// Respects path expansion/values in mpAttributePath
157 : std::optional<EndpointId> NextEndpointId();
158 :
159 : /// Checks if the given attributeId is valid for the current mOutputPath(endpoint/cluster)
160 : ///
161 : /// Meaning that it is known to the data model OR it is a always-there global attribute.
162 : bool IsValidAttributeId(AttributeId attributeId);
163 : };
164 :
165 : /// RollbackAttributePathExpandIterator is an AttributePathExpandIterator wrapper that rolls back the Next()
166 : /// call whenever a new `MarkCompleted()` method is not called.
167 : ///
168 : /// Example use cases:
169 : ///
170 : /// - Iterate over all attributes and process one-by-one, however when the iteration fails, resume at
171 : /// the last failure point:
172 : ///
173 : /// RollbackAttributePathExpandIterator iterator(....);
174 : /// ConcreteAttributePath path;
175 : ///
176 : /// for ( ; iterator.Next(path); iterator.MarkCompleted()) {
177 : /// if (!CanProcess(path)) {
178 : /// // iterator state IS PRESERVED so that Next() will return the SAME path on the next call.
179 : /// return CHIP_ERROR_TRY_AGAIN_LATER;
180 : /// }
181 : /// }
182 : ///
183 : /// - Grab what the next output path would be WITHOUT advancing a state;
184 : ///
185 : /// {
186 : /// RollbackAttributePathExpandIterator iterator(...., state);
187 : /// if (iterator.Next(...)) { ... }
188 : /// }
189 : /// // state here is ROLLED BACK (i.e. initializing a new iterator with it will start at the same place as the previous
190 : /// iteration attempt).
191 : ///
192 : ///
193 : class RollbackAttributePathExpandIterator
194 : {
195 : public:
196 1973 : RollbackAttributePathExpandIterator(DataModel::Provider * dataModel, AttributePathExpandIterator::Position & position) :
197 1973 : mAttributePathExpandIterator(dataModel, position), mPositionTarget(position), mCompletedPosition(position)
198 1973 : {}
199 1973 : ~RollbackAttributePathExpandIterator() { mPositionTarget = mCompletedPosition; }
200 :
201 6449 : bool Next(ConcreteAttributePath & path) { return mAttributePathExpandIterator.Next(path); }
202 :
203 : /// Marks the current iteration completed (so peek does not actually roll back)
204 4476 : void MarkCompleted() { mCompletedPosition = mPositionTarget; }
205 :
206 : private:
207 : AttributePathExpandIterator mAttributePathExpandIterator;
208 : AttributePathExpandIterator::Position & mPositionTarget;
209 : AttributePathExpandIterator::Position mCompletedPosition;
210 : };
211 :
212 : } // namespace app
213 : } // namespace chip
|