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 <lib/support/CodeUtils.h>
21 :
22 : using namespace chip::app::DataModel;
23 :
24 : namespace chip {
25 : namespace app {
26 :
27 3069 : AttributePathExpandIterator::AttributePathExpandIterator(DataModel::Provider * provider,
28 3069 : SingleLinkedListNode<AttributePathParams> * attributePath) :
29 3069 : mDataModelProvider(provider),
30 3069 : mpAttributePath(attributePath), mOutputPath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)
31 :
32 : {
33 3069 : mOutputPath.mExpanded = true; // this is reset in 'next' if needed
34 :
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 3069 : Next();
38 3069 : }
39 :
40 35 : bool AttributePathExpandIterator::IsValidAttributeId(AttributeId attributeId)
41 : {
42 35 : switch (attributeId)
43 : {
44 4 : case Clusters::Globals::Attributes::GeneratedCommandList::Id:
45 : case Clusters::Globals::Attributes::AcceptedCommandList::Id:
46 : case Clusters::Globals::Attributes::AttributeList::Id:
47 4 : return true;
48 31 : default:
49 31 : break;
50 : }
51 :
52 31 : const ConcreteAttributePath attributePath(mOutputPath.mEndpointId, mOutputPath.mClusterId, attributeId);
53 31 : return mDataModelProvider->GetAttributeInfo(attributePath).has_value();
54 : }
55 :
56 4375 : std::optional<AttributeId> AttributePathExpandIterator::NextAttributeId()
57 : {
58 4375 : if (mOutputPath.mAttributeId == kInvalidAttributeId)
59 : {
60 771 : if (mpAttributePath->mValue.HasWildcardAttributeId())
61 : {
62 736 : AttributeEntry entry = mDataModelProvider->FirstAttribute(mOutputPath);
63 736 : return entry.IsValid() //
64 : ? entry.path.mAttributeId //
65 736 : : Clusters::Globals::Attributes::GeneratedCommandList::Id; //
66 : }
67 :
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 35 : if (IsValidAttributeId(mpAttributePath->mValue.mAttributeId))
72 : {
73 26 : return mpAttributePath->mValue.mAttributeId;
74 : }
75 :
76 9 : return std::nullopt;
77 : }
78 :
79 : // advance the existing attribute id if it can be advanced
80 3604 : VerifyOrReturnValue(mpAttributePath->mValue.HasWildcardAttributeId(), std::nullopt);
81 :
82 : // Ensure (including ordering) that GlobalAttributesNotInMetadata is reported as needed
83 11650 : for (unsigned i = 0; i < ArraySize(GlobalAttributesNotInMetadata); i++)
84 : {
85 9408 : if (GlobalAttributesNotInMetadata[i] != mOutputPath.mAttributeId)
86 : {
87 8067 : continue;
88 : }
89 :
90 1341 : unsigned nextAttributeIndex = i + 1;
91 1341 : if (nextAttributeIndex < ArraySize(GlobalAttributesNotInMetadata))
92 : {
93 894 : return GlobalAttributesNotInMetadata[nextAttributeIndex];
94 : }
95 :
96 : // reached the end of global attributes
97 447 : return std::nullopt;
98 : }
99 :
100 2242 : AttributeEntry entry = mDataModelProvider->NextAttribute(mOutputPath);
101 2242 : if (entry.IsValid())
102 : {
103 1795 : return entry.path.mAttributeId;
104 : }
105 :
106 : // Finished the data model, start with global attributes
107 : static_assert(ArraySize(GlobalAttributesNotInMetadata) > 0);
108 447 : return GlobalAttributesNotInMetadata[0];
109 : }
110 :
111 1182 : std::optional<ClusterId> AttributePathExpandIterator::NextClusterId()
112 : {
113 :
114 1182 : if (mOutputPath.mClusterId == kInvalidClusterId)
115 : {
116 705 : if (mpAttributePath->mValue.HasWildcardClusterId())
117 : {
118 230 : ClusterEntry entry = mDataModelProvider->FirstServerCluster(mOutputPath.mEndpointId);
119 230 : return entry.IsValid() ? std::make_optional(entry.path.mClusterId) : std::nullopt;
120 : }
121 :
122 : // only return a cluster if it is valid
123 475 : const ConcreteClusterPath clusterPath(mOutputPath.mEndpointId, mpAttributePath->mValue.mClusterId);
124 475 : if (!mDataModelProvider->GetServerClusterInfo(clusterPath).has_value())
125 : {
126 29 : return std::nullopt;
127 : }
128 :
129 446 : return mpAttributePath->mValue.mClusterId;
130 : }
131 :
132 477 : VerifyOrReturnValue(mpAttributePath->mValue.HasWildcardClusterId(), std::nullopt);
133 :
134 242 : ClusterEntry entry = mDataModelProvider->NextServerCluster(mOutputPath);
135 242 : return entry.IsValid() ? std::make_optional(entry.path.mClusterId) : std::nullopt;
136 : }
137 :
138 1031 : std::optional<ClusterId> AttributePathExpandIterator::NextEndpointId()
139 : {
140 1031 : if (mOutputPath.mEndpointId == kInvalidEndpointId)
141 : {
142 617 : if (mpAttributePath->mValue.HasWildcardEndpointId())
143 : {
144 51 : EndpointEntry ep = mDataModelProvider->FirstEndpoint();
145 51 : return (ep.id != kInvalidEndpointId) ? std::make_optional(ep.id) : std::nullopt;
146 : }
147 :
148 566 : return mpAttributePath->mValue.mEndpointId;
149 : }
150 :
151 414 : VerifyOrReturnValue(mpAttributePath->mValue.HasWildcardEndpointId(), std::nullopt);
152 :
153 123 : EndpointEntry ep = mDataModelProvider->NextEndpoint(mOutputPath.mEndpointId);
154 123 : return (ep.id != kInvalidEndpointId) ? std::make_optional(ep.id) : std::nullopt;
155 : }
156 :
157 263 : 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 263 : VerifyOrReturn(mpAttributePath != nullptr && mpAttributePath->mValue.HasWildcardAttributeId());
162 :
163 : // Reset path expansion to ask for the first attribute of the current cluster
164 3 : mOutputPath.mAttributeId = kInvalidAttributeId;
165 3 : mOutputPath.mExpanded = true; // we know this is a wildcard attribute
166 3 : Next();
167 : }
168 :
169 6821 : bool AttributePathExpandIterator::AdvanceOutputPath()
170 : {
171 6821 : if (!mpAttributePath->mValue.IsWildcardPath())
172 : {
173 2597 : if (mOutputPath.mEndpointId != kInvalidEndpointId)
174 : {
175 951 : return false; // cannot expand non-wildcard path
176 : }
177 :
178 1646 : mOutputPath.mEndpointId = mpAttributePath->mValue.mEndpointId;
179 1646 : mOutputPath.mClusterId = mpAttributePath->mValue.mClusterId;
180 1646 : mOutputPath.mAttributeId = mpAttributePath->mValue.mAttributeId;
181 1646 : mOutputPath.mExpanded = false;
182 1646 : return true;
183 : }
184 :
185 : while (true)
186 : {
187 5697 : if (mOutputPath.mClusterId != kInvalidClusterId)
188 : {
189 :
190 4375 : std::optional<AttributeId> nextAttribute = NextAttributeId();
191 4375 : if (nextAttribute.has_value())
192 : {
193 3898 : mOutputPath.mAttributeId = *nextAttribute;
194 3898 : return true;
195 : }
196 : }
197 :
198 : // no valid attribute, try to advance the cluster, see if a suitable one exists
199 1799 : if (mOutputPath.mEndpointId != kInvalidEndpointId)
200 : {
201 1182 : std::optional<ClusterId> nextCluster = NextClusterId();
202 1182 : if (nextCluster.has_value())
203 : {
204 768 : mOutputPath.mClusterId = *nextCluster;
205 768 : mOutputPath.mAttributeId = kInvalidAttributeId; // restarts attributes
206 768 : continue;
207 : }
208 : }
209 :
210 : // no valid cluster, try advance the endpoint, see if a suitable on exists
211 1031 : std::optional<EndpointId> nextEndpoint = NextEndpointId();
212 1031 : if (nextEndpoint.has_value())
213 : {
214 705 : mOutputPath.mEndpointId = *nextEndpoint;
215 705 : mOutputPath.mClusterId = kInvalidClusterId; // restarts clusters
216 705 : continue;
217 : }
218 326 : return false;
219 1473 : }
220 : }
221 :
222 7627 : bool AttributePathExpandIterator::Next()
223 : {
224 8904 : while (mpAttributePath != nullptr)
225 : {
226 6821 : if (AdvanceOutputPath())
227 : {
228 5544 : return true;
229 : }
230 1277 : mpAttributePath = mpAttributePath->mpNext;
231 1277 : mOutputPath = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId);
232 1277 : mOutputPath.mExpanded = true; // this is reset to false on advancement if needed
233 : }
234 :
235 2083 : mOutputPath = ConcreteReadAttributePath();
236 2083 : return false;
237 : }
238 :
239 : } // namespace app
240 : } // namespace chip
|