Skip to content

Commit 222d5fc

Browse files
committed
Copied over the new AttributePathExpandIterator and will incrementally use it (so I can validate tests)
1 parent 2a686a7 commit 222d5fc

3 files changed

+851
-5
lines changed

src/app/AttributePathExpandIterator.cpp

+194-5
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ namespace app {
2626

2727
AttributePathExpandIterator::AttributePathExpandIterator(DataModel::Provider * provider,
2828
SingleLinkedListNode<AttributePathParams> * attributePath) :
29-
mDataModelProvider(provider),
30-
mpAttributePath(attributePath), mOutputPath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)
29+
mDataModelProvider(provider), mpAttributePath(attributePath),
30+
mOutputPath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)
3131

3232
{
3333
mOutputPath.mExpanded = true; // this is reset in 'next' if needed
@@ -165,7 +165,6 @@ void AttributePathExpandIterator::ResetCurrentCluster()
165165
mOutputPath.mExpanded = true; // we know this is a wildcard attribute
166166
Next();
167167
}
168-
169168
bool AttributePathExpandIterator::AdvanceOutputPath()
170169
{
171170
if (!mpAttributePath->mValue.IsWildcardPath())
@@ -227,14 +226,204 @@ bool AttributePathExpandIterator::Next()
227226
{
228227
return true;
229228
}
230-
mpAttributePath = mpAttributePath->mpNext;
231-
mOutputPath = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId);
229+
mpAttributePath = mpAttributePath->mpNext;
230+
mOutputPath = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId);
231+
232232
mOutputPath.mExpanded = true; // this is reset to false on advancement if needed
233233
}
234234

235235
mOutputPath = ConcreteReadAttributePath();
236236
return false;
237237
}
238238

239+
///// 2nd implementation
240+
241+
bool AttributePathExpandIterator2::AdvanceOutputPath()
242+
{
243+
if (!mState.mAttributePath->mValue.IsWildcardPath())
244+
{
245+
if (mState.mLastOutputPath.mEndpointId != kInvalidEndpointId)
246+
{
247+
return false; // cannot expand non-wildcard path
248+
}
249+
250+
mState.mLastOutputPath.mEndpointId = mState.mAttributePath->mValue.mEndpointId;
251+
mState.mLastOutputPath.mClusterId = mState.mAttributePath->mValue.mClusterId;
252+
mState.mLastOutputPath.mAttributeId = mState.mAttributePath->mValue.mAttributeId;
253+
mState.mLastOutputPath.mExpanded = false;
254+
return true;
255+
}
256+
257+
while (true)
258+
{
259+
if (mState.mLastOutputPath.mClusterId != kInvalidClusterId)
260+
{
261+
262+
std::optional<AttributeId> nextAttribute = NextAttributeId();
263+
if (nextAttribute.has_value())
264+
{
265+
mState.mLastOutputPath.mAttributeId = *nextAttribute;
266+
return true;
267+
}
268+
}
269+
270+
// no valid attribute, try to advance the cluster, see if a suitable one exists
271+
if (mState.mLastOutputPath.mEndpointId != kInvalidEndpointId)
272+
{
273+
std::optional<ClusterId> nextCluster = NextClusterId();
274+
if (nextCluster.has_value())
275+
{
276+
mState.mLastOutputPath.mClusterId = *nextCluster;
277+
mState.mLastOutputPath.mAttributeId = kInvalidAttributeId; // restarts attributes
278+
continue;
279+
}
280+
}
281+
282+
// no valid cluster, try advance the endpoint, see if a suitable on exists
283+
std::optional<EndpointId> nextEndpoint = NextEndpointId();
284+
if (nextEndpoint.has_value())
285+
{
286+
mState.mLastOutputPath.mEndpointId = *nextEndpoint;
287+
mState.mLastOutputPath.mClusterId = kInvalidClusterId; // restarts clusters
288+
continue;
289+
}
290+
return false;
291+
}
292+
}
293+
294+
bool AttributePathExpandIterator2::Next(ConcreteAttributePath & path)
295+
{
296+
while (mState.mAttributePath != nullptr)
297+
{
298+
if (AdvanceOutputPath())
299+
{
300+
path = mState.mLastOutputPath;
301+
return true;
302+
}
303+
mState.mAttributePath = mState.mAttributePath->mpNext;
304+
mState.mLastOutputPath = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId);
305+
mState.mLastOutputPath.mExpanded = true; // this is reset to false on advancement if needed
306+
}
307+
308+
return false;
309+
}
310+
311+
bool AttributePathExpandIterator2::IsValidAttributeId(AttributeId attributeId)
312+
{
313+
switch (attributeId)
314+
{
315+
case Clusters::Globals::Attributes::GeneratedCommandList::Id:
316+
case Clusters::Globals::Attributes::AcceptedCommandList::Id:
317+
case Clusters::Globals::Attributes::AttributeList::Id:
318+
return true;
319+
default:
320+
break;
321+
}
322+
323+
const ConcreteAttributePath attributePath(mState.mLastOutputPath.mEndpointId, mState.mLastOutputPath.mClusterId, attributeId);
324+
return mDataModelProvider->GetAttributeInfo(attributePath).has_value();
325+
}
326+
327+
std::optional<AttributeId> AttributePathExpandIterator2::NextAttributeId()
328+
{
329+
if (mState.mLastOutputPath.mAttributeId == kInvalidAttributeId)
330+
{
331+
if (mState.mAttributePath->mValue.HasWildcardAttributeId())
332+
{
333+
AttributeEntry entry = mDataModelProvider->FirstAttribute(mState.mLastOutputPath);
334+
return entry.IsValid() //
335+
? entry.path.mAttributeId //
336+
: Clusters::Globals::Attributes::GeneratedCommandList::Id; //
337+
}
338+
339+
// We allow fixed attribute IDs if and only if they are valid:
340+
// - they may be GLOBAL attributes OR
341+
// - they are valid attributes for this cluster
342+
if (IsValidAttributeId(mState.mAttributePath->mValue.mAttributeId))
343+
{
344+
return mState.mAttributePath->mValue.mAttributeId;
345+
}
346+
347+
return std::nullopt;
348+
}
349+
350+
// advance the existing attribute id if it can be advanced
351+
VerifyOrReturnValue(mState.mAttributePath->mValue.HasWildcardAttributeId(), std::nullopt);
352+
353+
// Ensure (including ordering) that GlobalAttributesNotInMetadata is reported as needed
354+
for (unsigned i = 0; i < ArraySize(GlobalAttributesNotInMetadata); i++)
355+
{
356+
if (GlobalAttributesNotInMetadata[i] != mState.mLastOutputPath.mAttributeId)
357+
{
358+
continue;
359+
}
360+
361+
unsigned nextAttributeIndex = i + 1;
362+
if (nextAttributeIndex < ArraySize(GlobalAttributesNotInMetadata))
363+
{
364+
return GlobalAttributesNotInMetadata[nextAttributeIndex];
365+
}
366+
367+
// reached the end of global attributes
368+
return std::nullopt;
369+
}
370+
371+
AttributeEntry entry = mDataModelProvider->NextAttribute(mState.mLastOutputPath);
372+
if (entry.IsValid())
373+
{
374+
return entry.path.mAttributeId;
375+
}
376+
377+
// Finished the data model, start with global attributes
378+
static_assert(ArraySize(GlobalAttributesNotInMetadata) > 0);
379+
return GlobalAttributesNotInMetadata[0];
380+
}
381+
382+
std::optional<ClusterId> AttributePathExpandIterator2::NextClusterId()
383+
{
384+
385+
if (mState.mLastOutputPath.mClusterId == kInvalidClusterId)
386+
{
387+
if (mState.mAttributePath->mValue.HasWildcardClusterId())
388+
{
389+
ClusterEntry entry = mDataModelProvider->FirstServerCluster(mState.mLastOutputPath.mEndpointId);
390+
return entry.IsValid() ? std::make_optional(entry.path.mClusterId) : std::nullopt;
391+
}
392+
393+
// only return a cluster if it is valid
394+
const ConcreteClusterPath clusterPath(mState.mLastOutputPath.mEndpointId, mState.mAttributePath->mValue.mClusterId);
395+
if (!mDataModelProvider->GetServerClusterInfo(clusterPath).has_value())
396+
{
397+
return std::nullopt;
398+
}
399+
400+
return mState.mAttributePath->mValue.mClusterId;
401+
}
402+
403+
VerifyOrReturnValue(mState.mAttributePath->mValue.HasWildcardClusterId(), std::nullopt);
404+
405+
ClusterEntry entry = mDataModelProvider->NextServerCluster(mState.mLastOutputPath);
406+
return entry.IsValid() ? std::make_optional(entry.path.mClusterId) : std::nullopt;
407+
}
408+
409+
std::optional<ClusterId> AttributePathExpandIterator2::NextEndpointId()
410+
{
411+
if (mState.mLastOutputPath.mEndpointId == kInvalidEndpointId)
412+
{
413+
if (mState.mAttributePath->mValue.HasWildcardEndpointId())
414+
{
415+
EndpointEntry ep = mDataModelProvider->FirstEndpoint();
416+
return (ep.id != kInvalidEndpointId) ? std::make_optional(ep.id) : std::nullopt;
417+
}
418+
419+
return mState.mAttributePath->mValue.mEndpointId;
420+
}
421+
422+
VerifyOrReturnValue(mState.mAttributePath->mValue.HasWildcardEndpointId(), std::nullopt);
423+
424+
EndpointEntry ep = mDataModelProvider->NextEndpoint(mState.mLastOutputPath.mEndpointId);
425+
return (ep.id != kInvalidEndpointId) ? std::make_optional(ep.id) : std::nullopt;
426+
}
427+
239428
} // namespace app
240429
} // namespace chip

src/app/AttributePathExpandIterator.h

+133
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,144 @@
2020
#include <app/AttributePathParams.h>
2121
#include <app/ConcreteAttributePath.h>
2222
#include <app/data-model-provider/Provider.h>
23+
#include <lib/core/DataModelTypes.h>
2324
#include <lib/support/LinkedList.h>
2425

2526
namespace chip {
2627
namespace app {
2728

29+
/// Handles attribute path expansions
30+
/// Usage:
31+
///
32+
/// - Start iterating by creating an iteration state
33+
///
34+
/// mState = AttributePathExpandIterator2::State::StartIterating(path);
35+
///
36+
/// - Use the iteration state in a for loop:
37+
///
38+
/// ConcreteAttributePath path;
39+
/// for (AttributePathExpandIterator2 iterator(mState); iterator->Next(path);) {
40+
/// // use `path` here`
41+
/// }
42+
///
43+
/// OR:
44+
///
45+
/// ConcreteAttributePath path;
46+
/// AttributePathExpandIterator2 iterator(mState);
47+
///
48+
/// while (iterator.Next(path)) {
49+
/// // use `path` here`
50+
/// }
51+
///
52+
/// USAGE requirements and assumptions:
53+
///
54+
/// - There should be only one single AttributePathExpandIterator2 for a state at a time.
55+
///
56+
/// - `State` is automatically updated by the AttributePathExpandIterator2, so
57+
/// calling `Next` on the iterator will update the state variable.
58+
///
59+
///
60+
class AttributePathExpandIterator2
61+
{
62+
public:
63+
class State
64+
{
65+
public:
66+
friend class AttributePathExpandIterator2;
67+
68+
/// External callers can only ever start iterating on a new path from the beginning
69+
static State StartIterating(SingleLinkedListNode<AttributePathParams> * path) { return State(path); }
70+
71+
/// Copies are allowed
72+
State(const State &) = default;
73+
State & operator=(const State &) = default;
74+
75+
State() : mAttributePath(nullptr) {}
76+
77+
/// Reset the iterator to the beginning of current cluster if we are in the middle of expanding a wildcard attribute id for
78+
/// some cluster.
79+
///
80+
/// When attributes are changed in the middle of expanding a wildcard attribute, we need to reset the iterator, to provide
81+
/// the client with a consistent state of the cluster.
82+
void IterateFromTheStartOfTheCurrentCluster()
83+
{
84+
VerifyOrReturn(mAttributePath != nullptr && mAttributePath->mValue.HasWildcardAttributeId());
85+
mLastOutputPath.mAttributeId = kInvalidAttributeId;
86+
mLastOutputPath.mExpanded = true;
87+
}
88+
89+
/// Fetch the last output path by this expand iterator. MAY contain an invalid path (i.e. using
90+
/// kInvalidEndpointId/kInvalidClusterId/kInvalidAttributeId) in case the expansion was not started (i.e. Next on a linked
91+
/// iterator was never called)
92+
///
93+
/// Returns false if the current iteration is completed
94+
bool GetLastOutputPath(ConcreteAttributePath & path)
95+
{
96+
path = mLastOutputPath;
97+
return (mAttributePath != nullptr);
98+
}
99+
100+
protected:
101+
State(SingleLinkedListNode<AttributePathParams> * path) :
102+
mAttributePath(path), mLastOutputPath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)
103+
{
104+
mLastOutputPath.mExpanded = true;
105+
}
106+
107+
SingleLinkedListNode<AttributePathParams> * mAttributePath;
108+
ConcreteAttributePath mLastOutputPath;
109+
};
110+
111+
AttributePathExpandIterator2(DataModel::Provider * dataModel, State & state) : mDataModelProvider(dataModel), mState(state) {}
112+
113+
// this class may not be copied. A new one should be created when needed and they
114+
// should not overlap
115+
AttributePathExpandIterator2(const AttributePathExpandIterator2 &) = delete;
116+
AttributePathExpandIterator2 & operator=(const AttributePathExpandIterator2 &) = delete;
117+
118+
/// Get the next path of the expansion (if one exists).
119+
///
120+
/// On success, true is returned and `path` is filled with the next path in the
121+
/// expansion.
122+
/// On iteration completion, false is returned and the content of path IS NOT DEFINED.
123+
bool Next(ConcreteAttributePath & path);
124+
125+
private:
126+
DataModel::Provider * mDataModelProvider;
127+
State & mState;
128+
129+
/// Move to the next endpoint/cluster/attribute triplet that is valid given
130+
/// the current mOutputPath and mpAttributePath
131+
///
132+
/// returns true if such a next value was found.
133+
bool AdvanceOutputPath();
134+
135+
/// Get the next attribute ID in mOutputPath(endpoint/cluster) if one is available.
136+
/// Will start from the beginning if current mOutputPath.mAttributeId is kInvalidAttributeId
137+
///
138+
/// Respects path expansion/values in mpAttributePath
139+
///
140+
/// Handles Global attributes (which are returned at the end)
141+
std::optional<AttributeId> NextAttributeId();
142+
143+
/// Get the next cluster ID in mOutputPath(endpoint) if one is available.
144+
/// Will start from the beginning if current mOutputPath.mClusterId is kInvalidClusterId
145+
///
146+
/// Respects path expansion/values in mpAttributePath
147+
std::optional<ClusterId> NextClusterId();
148+
149+
/// Get the next endpoint ID in mOutputPath if one is available.
150+
/// Will start from the beginning if current mOutputPath.mEndpointId is kInvalidEndpointId
151+
///
152+
/// Respects path expansion/values in mpAttributePath
153+
std::optional<ClusterId> NextEndpointId();
154+
155+
/// Checks if the given attributeId is valid for the current mOutputPath(endpoint/cluster)
156+
///
157+
/// Meaning that it is known to the data model OR it is a always-there global attribute.
158+
bool IsValidAttributeId(AttributeId attributeId);
159+
};
160+
28161
/**
29162
* AttributePathExpandIterator is used to iterate over a linked list of AttributePathParams-s.
30163
* The AttributePathExpandIterator is copiable, however, the given cluster info must be valid when calling Next().

0 commit comments

Comments
 (0)