19
19
#include < app/GlobalAttributes.h>
20
20
#include < lib/support/CodeUtils.h>
21
21
22
+ #include < optional>
23
+
22
24
using namespace chip ::app::DataModel;
23
25
24
26
namespace chip {
25
27
namespace app {
26
28
27
- AttributePathExpandIterator::AttributePathExpandIterator (DataModel::Provider * provider,
28
- SingleLinkedListNode<AttributePathParams> * attributePath) :
29
- mDataModelProvider (provider),
30
- mpAttributePath (attributePath), mOutputPath (kInvalidEndpointId , kInvalidClusterId , kInvalidAttributeId )
29
+ bool AttributePathExpandIterator::AdvanceOutputPath ()
30
+ {
31
+ // / Output path invariants
32
+ // / - kInvalid* constants are used to define "no value available (yet)" and
33
+ // / iteration loop will fill the first value when such a value is seen (fixed for non-wildcard
34
+ // / or iteration-based in case of wildcards).
35
+ // / - Iteration of the output path is done in order: first endpoint, then cluster, then attribute.
36
+ // / Processing works like:
37
+ // / - Initial state is kInvalidEndpointId/kInvalidClusterId/kInvalidAttributeId
38
+ // / - First loop pass fills-in endpointID, followed by clusterID, followed by attributeID
39
+ // / - Whenever one level is done iterating (there is no "next") the following
40
+ // / "higher path component" is updated:
41
+ // / - once a valid path exists, try to advance attributeID
42
+ // / - if attributeID fails to advance, try to advance clusterID (and restart attributeID)
43
+ // / - if clusterID fails to advance, try to advance endpointID (and restart clusterID)
44
+ // / - if endpointID fails to advance, iteration is done
45
+ while (true )
46
+ {
47
+ if (mPosition .mOutputPath .mClusterId != kInvalidClusterId )
48
+ {
49
+ std::optional<AttributeId> nextAttribute = NextAttributeId ();
50
+ if (nextAttribute.has_value ())
51
+ {
52
+ mPosition .mOutputPath .mAttributeId = *nextAttribute;
53
+ mPosition .mOutputPath .mExpanded = mPosition .mAttributePath ->mValue .IsWildcardPath ();
54
+ return true ;
55
+ }
56
+ }
57
+
58
+ // no valid attribute, try to advance the cluster, see if a suitable one exists
59
+ if (mPosition .mOutputPath .mEndpointId != kInvalidEndpointId )
60
+ {
61
+ std::optional<ClusterId> nextCluster = NextClusterId ();
62
+ if (nextCluster.has_value ())
63
+ {
64
+ // A new cluster ID is to be processed. This sets the cluster ID to the new value and
65
+ // ALSO resets the attribute ID to "invalid", to trigger an attribute set/expansion from
66
+ // the beginning.
67
+ mPosition .mOutputPath .mClusterId = *nextCluster;
68
+ mPosition .mOutputPath .mAttributeId = kInvalidAttributeId ;
69
+ continue ;
70
+ }
71
+ }
72
+
73
+ // No valid cluster, try advance the endpoint, see if a suitable one exists.
74
+ std::optional<EndpointId> nextEndpoint = NextEndpointId ();
75
+ if (nextEndpoint.has_value ())
76
+ {
77
+ // A new endpoint ID is to be processed. This sets the endpoint ID to the new value and
78
+ // ALSO resets the cluster ID to "invalid", to trigger a cluster set/expansion from
79
+ // the beginning.
80
+ mPosition .mOutputPath .mEndpointId = *nextEndpoint;
81
+ mPosition .mOutputPath .mClusterId = kInvalidClusterId ;
82
+ continue ;
83
+ }
84
+ return false ;
85
+ }
86
+ }
31
87
88
+ bool AttributePathExpandIterator::Next (ConcreteAttributePath & path)
32
89
{
33
- mOutputPath .mExpanded = true ; // this is reset in 'next' if needed
90
+ while (mPosition .mAttributePath != nullptr )
91
+ {
92
+ if (AdvanceOutputPath ())
93
+ {
94
+ path = mPosition .mOutputPath ;
95
+ return true ;
96
+ }
97
+ mPosition .mAttributePath = mPosition .mAttributePath ->mpNext ;
98
+ mPosition .mOutputPath = ConcreteReadAttributePath (kInvalidEndpointId , kInvalidClusterId , kInvalidAttributeId );
99
+ }
34
100
35
- // Make the iterator ready to emit the first valid path in the list.
36
- // TODO: the bool return value here is completely unchecked
37
- Next ();
101
+ return false ;
38
102
}
39
103
40
104
bool AttributePathExpandIterator::IsValidAttributeId (AttributeId attributeId)
@@ -49,40 +113,43 @@ bool AttributePathExpandIterator::IsValidAttributeId(AttributeId attributeId)
49
113
break ;
50
114
}
51
115
52
- const ConcreteAttributePath attributePath (mOutputPath .mEndpointId , mOutputPath .mClusterId , attributeId);
116
+ const ConcreteAttributePath attributePath (mPosition . mOutputPath .mEndpointId , mPosition . mOutputPath .mClusterId , attributeId);
53
117
return mDataModelProvider ->GetAttributeInfo (attributePath).has_value ();
54
118
}
55
119
56
120
std::optional<AttributeId> AttributePathExpandIterator::NextAttributeId ()
57
121
{
58
- if (mOutputPath .mAttributeId == kInvalidAttributeId )
122
+ if (mPosition . mOutputPath .mAttributeId == kInvalidAttributeId )
59
123
{
60
- if (mpAttributePath ->mValue .HasWildcardAttributeId ())
124
+ if (mPosition . mAttributePath ->mValue .HasWildcardAttributeId ())
61
125
{
62
- AttributeEntry entry = mDataModelProvider ->FirstAttribute (mOutputPath );
126
+ AttributeEntry entry = mDataModelProvider ->FirstAttribute (mPosition . mOutputPath );
63
127
return entry.IsValid () //
64
128
? entry.path .mAttributeId //
65
129
: Clusters::Globals::Attributes::GeneratedCommandList::Id; //
66
130
}
67
131
68
- // We allow fixed attribute IDs if and only if they are valid:
69
- // - they may be GLOBAL attributes OR
70
- // - they are valid attributes for this cluster
71
- if (IsValidAttributeId (mpAttributePath->mValue .mAttributeId ))
132
+ // At this point, the attributeID is NOT a wildcard (i.e. it is fixed).
133
+ //
134
+ // For wildcard expansion, we validate that this is a valid attribute for the given
135
+ // cluster on the given endpoint. If not a wildcard expansion, return it as-is.
136
+ if (mPosition .mAttributePath ->mValue .IsWildcardPath ())
72
137
{
73
- return mpAttributePath->mValue .mAttributeId ;
138
+ if (!IsValidAttributeId (mPosition .mAttributePath ->mValue .mAttributeId ))
139
+ {
140
+ return std::nullopt;
141
+ }
74
142
}
75
-
76
- return std::nullopt;
143
+ return mPosition .mAttributePath ->mValue .mAttributeId ;
77
144
}
78
145
79
- // advance the existing attribute id if it can be advanced
80
- VerifyOrReturnValue (mpAttributePath ->mValue .HasWildcardAttributeId (), std::nullopt);
146
+ // Advance the existing attribute id if it can be advanced.
147
+ VerifyOrReturnValue (mPosition . mAttributePath ->mValue .HasWildcardAttributeId (), std::nullopt);
81
148
82
149
// Ensure (including ordering) that GlobalAttributesNotInMetadata is reported as needed
83
150
for (unsigned i = 0 ; i < ArraySize (GlobalAttributesNotInMetadata); i++)
84
151
{
85
- if (GlobalAttributesNotInMetadata[i] != mOutputPath .mAttributeId )
152
+ if (GlobalAttributesNotInMetadata[i] != mPosition . mOutputPath .mAttributeId )
86
153
{
87
154
continue ;
88
155
}
@@ -93,11 +160,12 @@ std::optional<AttributeId> AttributePathExpandIterator::NextAttributeId()
93
160
return GlobalAttributesNotInMetadata[nextAttributeIndex];
94
161
}
95
162
96
- // reached the end of global attributes
163
+ // Reached the end of global attributes. Since global attributes are
164
+ // reported last, finishing global attributes means everything completed.
97
165
return std::nullopt;
98
166
}
99
167
100
- AttributeEntry entry = mDataModelProvider ->NextAttribute (mOutputPath );
168
+ AttributeEntry entry = mDataModelProvider ->NextAttribute (mPosition . mOutputPath );
101
169
if (entry.IsValid ())
102
170
{
103
171
return entry.path .mAttributeId ;
@@ -111,130 +179,55 @@ std::optional<AttributeId> AttributePathExpandIterator::NextAttributeId()
111
179
std::optional<ClusterId> AttributePathExpandIterator::NextClusterId ()
112
180
{
113
181
114
- if (mOutputPath .mClusterId == kInvalidClusterId )
182
+ if (mPosition . mOutputPath .mClusterId == kInvalidClusterId )
115
183
{
116
- if (mpAttributePath ->mValue .HasWildcardClusterId ())
184
+ if (mPosition . mAttributePath ->mValue .HasWildcardClusterId ())
117
185
{
118
- ClusterEntry entry = mDataModelProvider ->FirstServerCluster (mOutputPath .mEndpointId );
186
+ ClusterEntry entry = mDataModelProvider ->FirstServerCluster (mPosition . mOutputPath .mEndpointId );
119
187
return entry.IsValid () ? std::make_optional (entry.path .mClusterId ) : std::nullopt;
120
188
}
121
189
122
- // only return a cluster if it is valid
123
- const ConcreteClusterPath clusterPath (mOutputPath .mEndpointId , mpAttributePath->mValue .mClusterId );
124
- if (!mDataModelProvider ->GetServerClusterInfo (clusterPath).has_value ())
190
+ // At this point, the clusterID is NOT a wildcard (i.e. is fixed).
191
+ //
192
+ // For wildcard expansion, we validate that this is a valid cluster for the endpoint.
193
+ // If non-wildcard expansion, we return as-is.
194
+ if (mPosition .mAttributePath ->mValue .IsWildcardPath ())
125
195
{
126
- return std::nullopt;
196
+ const ConcreteClusterPath clusterPath (mPosition .mOutputPath .mEndpointId , mPosition .mAttributePath ->mValue .mClusterId );
197
+ if (!mDataModelProvider ->GetServerClusterInfo (clusterPath).has_value ())
198
+ {
199
+ return std::nullopt;
200
+ }
127
201
}
128
202
129
- return mpAttributePath ->mValue .mClusterId ;
203
+ return mPosition . mAttributePath ->mValue .mClusterId ;
130
204
}
131
205
132
- VerifyOrReturnValue (mpAttributePath ->mValue .HasWildcardClusterId (), std::nullopt);
206
+ VerifyOrReturnValue (mPosition . mAttributePath ->mValue .HasWildcardClusterId (), std::nullopt);
133
207
134
- ClusterEntry entry = mDataModelProvider ->NextServerCluster (mOutputPath );
208
+ ClusterEntry entry = mDataModelProvider ->NextServerCluster (mPosition . mOutputPath );
135
209
return entry.IsValid () ? std::make_optional (entry.path .mClusterId ) : std::nullopt;
136
210
}
137
211
138
212
std::optional<ClusterId> AttributePathExpandIterator::NextEndpointId ()
139
213
{
140
- if (mOutputPath .mEndpointId == kInvalidEndpointId )
214
+ if (mPosition . mOutputPath .mEndpointId == kInvalidEndpointId )
141
215
{
142
- if (mpAttributePath ->mValue .HasWildcardEndpointId ())
216
+ if (mPosition . mAttributePath ->mValue .HasWildcardEndpointId ())
143
217
{
144
218
EndpointEntry ep = mDataModelProvider ->FirstEndpoint ();
145
219
return (ep.id != kInvalidEndpointId ) ? std::make_optional (ep.id ) : std::nullopt;
146
220
}
147
221
148
- return mpAttributePath ->mValue .mEndpointId ;
222
+ return mPosition . mAttributePath ->mValue .mEndpointId ;
149
223
}
150
224
151
- VerifyOrReturnValue (mpAttributePath->mValue .HasWildcardEndpointId (), std::nullopt);
225
+ // Expand endpoints only if it is a wildcard on the endpoint specifically.
226
+ VerifyOrReturnValue (mPosition .mAttributePath ->mValue .HasWildcardEndpointId (), std::nullopt);
152
227
153
- EndpointEntry ep = mDataModelProvider ->NextEndpoint (mOutputPath .mEndpointId );
228
+ EndpointEntry ep = mDataModelProvider ->NextEndpoint (mPosition . mOutputPath .mEndpointId );
154
229
return (ep.id != kInvalidEndpointId ) ? std::make_optional (ep.id ) : std::nullopt;
155
230
}
156
231
157
- void AttributePathExpandIterator::ResetCurrentCluster ()
158
- {
159
- // If this is a null iterator, or the attribute id of current cluster info is not a wildcard attribute id, then this function
160
- // will do nothing, since we won't be expanding the wildcard attribute ids under a cluster.
161
- VerifyOrReturn (mpAttributePath != nullptr && mpAttributePath->mValue .HasWildcardAttributeId ());
162
-
163
- // Reset path expansion to ask for the first attribute of the current cluster
164
- mOutputPath .mAttributeId = kInvalidAttributeId ;
165
- mOutputPath .mExpanded = true ; // we know this is a wildcard attribute
166
- Next ();
167
- }
168
-
169
- bool AttributePathExpandIterator::AdvanceOutputPath ()
170
- {
171
- if (!mpAttributePath->mValue .IsWildcardPath ())
172
- {
173
- if (mOutputPath .mEndpointId != kInvalidEndpointId )
174
- {
175
- return false ; // cannot expand non-wildcard path
176
- }
177
-
178
- mOutputPath .mEndpointId = mpAttributePath->mValue .mEndpointId ;
179
- mOutputPath .mClusterId = mpAttributePath->mValue .mClusterId ;
180
- mOutputPath .mAttributeId = mpAttributePath->mValue .mAttributeId ;
181
- mOutputPath .mExpanded = false ;
182
- return true ;
183
- }
184
-
185
- while (true )
186
- {
187
- if (mOutputPath .mClusterId != kInvalidClusterId )
188
- {
189
-
190
- std::optional<AttributeId> nextAttribute = NextAttributeId ();
191
- if (nextAttribute.has_value ())
192
- {
193
- mOutputPath .mAttributeId = *nextAttribute;
194
- return true ;
195
- }
196
- }
197
-
198
- // no valid attribute, try to advance the cluster, see if a suitable one exists
199
- if (mOutputPath .mEndpointId != kInvalidEndpointId )
200
- {
201
- std::optional<ClusterId> nextCluster = NextClusterId ();
202
- if (nextCluster.has_value ())
203
- {
204
- mOutputPath .mClusterId = *nextCluster;
205
- mOutputPath .mAttributeId = kInvalidAttributeId ; // restarts attributes
206
- continue ;
207
- }
208
- }
209
-
210
- // no valid cluster, try advance the endpoint, see if a suitable on exists
211
- std::optional<EndpointId> nextEndpoint = NextEndpointId ();
212
- if (nextEndpoint.has_value ())
213
- {
214
- mOutputPath .mEndpointId = *nextEndpoint;
215
- mOutputPath .mClusterId = kInvalidClusterId ; // restarts clusters
216
- continue ;
217
- }
218
- return false ;
219
- }
220
- }
221
-
222
- bool AttributePathExpandIterator::Next ()
223
- {
224
- while (mpAttributePath != nullptr )
225
- {
226
- if (AdvanceOutputPath ())
227
- {
228
- return true ;
229
- }
230
- mpAttributePath = mpAttributePath->mpNext ;
231
- mOutputPath = ConcreteReadAttributePath (kInvalidEndpointId , kInvalidClusterId , kInvalidAttributeId );
232
- mOutputPath .mExpanded = true ; // this is reset to false on advancement if needed
233
- }
234
-
235
- mOutputPath = ConcreteReadAttributePath ();
236
- return false ;
237
- }
238
-
239
232
} // namespace app
240
233
} // namespace chip
0 commit comments