• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/syntax/lazy_for_each_builder.h"
17 #include "core/components_ng/pattern/recycle_view/recycle_dummy_node.h"
18 
19 namespace OHOS::Ace::NG {
GetChildByIndex(int32_t index,bool needBuild,bool isCache)20     std::pair<std::string, RefPtr<UINode>> LazyForEachBuilder::GetChildByIndex(
21         int32_t index, bool needBuild, bool isCache)
22     {
23         auto iter = cachedItems_.find(index);
24         if (iter != cachedItems_.end()) {
25             if (iter->second.second) {
26                 return iter->second;
27             }
28             auto keyIter = expiringItem_.find(iter->second.first);
29             if (keyIter != expiringItem_.end() && keyIter->second.second) {
30                 if (!isCache) {
31                     iter->second.second = keyIter->second.second;
32                     expiringItem_.erase(keyIter);
33                     return iter->second;
34                 } else {
35                     return { keyIter->first, keyIter->second.second };
36                 }
37             }
38         }
39 
40         if (needBuild) {
41             ACE_SCOPED_TRACE("Builder:BuildLazyItem [%d]", index);
42             std::pair<std::string, RefPtr<UINode>> itemInfo;
43             if (useNewInterface_) {
44                 itemInfo = OnGetChildByIndexNew(ConvertFromToIndex(index), cachedItems_, expiringItem_);
45             } else {
46                 itemInfo = OnGetChildByIndex(ConvertFromToIndex(index), expiringItem_);
47             }
48             CHECK_NULL_RETURN(itemInfo.second, itemInfo);
49             if (isCache) {
50                 expiringItem_.emplace(itemInfo.first, LazyForEachCacheChild(index, itemInfo.second));
51                 cachedItems_[index] = LazyForEachChild(itemInfo.first, nullptr);
52             } else {
53                 cachedItems_[index] = itemInfo;
54             }
55             return itemInfo;
56         }
57         return {};
58     }
59 
OnDataReloaded()60     void LazyForEachBuilder::OnDataReloaded()
61     {
62         for (auto& [key, node] : expiringItem_) {
63             node.first = -1;
64         }
65         for (auto& [index, node] : cachedItems_) {
66             if (node.second) {
67                 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(-1, std::move(node.second)));
68             }
69         }
70         cachedItems_.clear();
71         needTransition = true;
72     }
73 
OnDataAdded(size_t index)74     bool LazyForEachBuilder::OnDataAdded(size_t index)
75     {
76         NotifyDataAdded(index);
77         if (!cachedItems_.empty() && index <= static_cast<size_t>(cachedItems_.rbegin()->first)) {
78             decltype(cachedItems_) temp(std::move(cachedItems_));
79 
80             for (auto& [oldindex, id] : temp) {
81                 cachedItems_.try_emplace(
82                     index > static_cast<size_t>(oldindex) ? oldindex : oldindex + 1, std::move(id));
83             }
84         }
85         for (auto& [key, node] : expiringItem_) {
86             if (static_cast<size_t>(node.first) >= index && node.first != -1) {
87                 node.first++;
88             }
89         }
90 
91         return true;
92     }
93 
OnDataBulkAdded(size_t index,size_t count)94     bool LazyForEachBuilder::OnDataBulkAdded(size_t index, size_t count)
95     {
96         if (!cachedItems_.empty() && index <= static_cast<size_t>(cachedItems_.rbegin()->first)) {
97             decltype(cachedItems_) temp(std::move(cachedItems_));
98 
99             for (auto& [oldindex, id] : temp) {
100                 cachedItems_.try_emplace(
101                     index > static_cast<size_t>(oldindex) ? oldindex : oldindex + count, std::move(id));
102             }
103         }
104         for (auto& [key, node] : expiringItem_) {
105             if (static_cast<size_t>(node.first) >= index && node.first != -1) {
106                 node.first = node.first + static_cast<int32_t>(count);
107             }
108         }
109 
110         return true;
111     }
112 
OnDataDeleted(size_t index)113     RefPtr<UINode> LazyForEachBuilder::OnDataDeleted(size_t index)
114     {
115         RefPtr<UINode> node;
116         if (cachedItems_.empty()) {
117             return node;
118         }
119         if (index <= static_cast<size_t>(cachedItems_.rbegin()->first)) {
120             decltype(cachedItems_) temp(std::move(cachedItems_));
121 
122             for (auto& [oldindex, child] : temp) {
123                 if (static_cast<size_t>(oldindex) == index) {
124                     node = child.second;
125                     KeepRemovedItemInCache(child, expiringItem_);
126                 } else {
127                     cachedItems_.try_emplace(
128                         index > static_cast<size_t>(oldindex) ? oldindex : oldindex - 1, std::move(child));
129                 }
130             }
131         }
132         NotifyDataDeleted(node, index, false);
133         for (auto& [key, child] : expiringItem_) {
134             if (static_cast<size_t>(child.first) > index) {
135                 child.first--;
136                 continue;
137             }
138             if (static_cast<size_t>(child.first) == index) {
139                 child.first = -1;
140                 node = child.second;
141             }
142         }
143 
144         return node;
145     }
146 
OnDataBulkDeleted(size_t index,size_t count)147     std::list<std::pair<std::string, RefPtr<UINode>>>& LazyForEachBuilder::OnDataBulkDeleted(size_t index, size_t count)
148     {
149         if (cachedItems_.empty()) {
150             return nodeList_;
151         }
152         if (index <= static_cast<size_t>(cachedItems_.rbegin()->first)) {
153             decltype(cachedItems_) temp(std::move(cachedItems_));
154 
155             for (auto& [oldindex, child] : temp) {
156                 if (static_cast<size_t>(oldindex) >= index && static_cast<size_t>(oldindex) < index + count) {
157                     nodeList_.emplace_back(child.first, child.second);
158                 } else {
159                     cachedItems_.try_emplace(
160                         index > static_cast<size_t>(oldindex) ? oldindex : oldindex - count, std::move(child));
161                 }
162             }
163         }
164 
165         if (DeleteExpiringItemImmediately()) {
166             decltype(expiringItem_) expiringTemp(std::move(expiringItem_));
167             for (auto& [key, child] : expiringTemp) {
168                 if (child.first < 0) {
169                     nodeList_.emplace_back(key, child.second);
170                     continue;
171                 }
172                 if (static_cast<size_t>(child.first) >= index + count) {
173                     child.first -= static_cast<int32_t>(count);
174                     expiringItem_.try_emplace(key, child);
175                     continue;
176                 }
177                 if (static_cast<size_t>(child.first) >= index && static_cast<size_t>(child.first) < index + count) {
178                     nodeList_.emplace_back(key, child.second);
179                 } else {
180                     expiringItem_.try_emplace(key, child);
181                 }
182             }
183         } else {
184             for (auto& [key, child] : expiringItem_) {
185                 if (static_cast<size_t>(child.first) >= index + count) {
186                     child.first -= static_cast<int32_t>(count);
187                     continue;
188                 }
189                 if (static_cast<size_t>(child.first) >= index && static_cast<size_t>(child.first) < index + count) {
190                     child.first = -1;
191                 }
192             }
193         }
194 
195         return nodeList_;
196     }
197 
OnDataChanged(size_t index)198     bool LazyForEachBuilder::OnDataChanged(size_t index)
199     {
200         auto keyIter = cachedItems_.find(index);
201         if (keyIter != cachedItems_.end()) {
202             if (keyIter->second.second) {
203                 NotifyDataChanged(index, keyIter->second.second, false);
204                 expiringItem_.try_emplace(
205                     keyIter->second.first, LazyForEachCacheChild(-1, std::move(keyIter->second.second)));
206             } else {
207                 InvalidIndexOfChangedData(index);
208             }
209             cachedItems_.erase(keyIter);
210             return true;
211         }
212         return false;
213     }
214 
OnDataBulkChanged(size_t index,size_t count)215     std::list<std::pair<std::string, RefPtr<UINode>>>& LazyForEachBuilder::OnDataBulkChanged(size_t index, size_t count)
216     {
217         if (cachedItems_.empty()) {
218             return nodeList_;
219         }
220         if (static_cast<size_t>(cachedItems_.rbegin()->first) < index) {
221             return nodeList_;
222         }
223         auto iter = cachedItems_.begin();
224         while (iter != cachedItems_.end()) {
225             auto itemIndex = iter->first;
226             const auto& child = iter->second;
227             if (static_cast<size_t>(itemIndex) >= index && static_cast<size_t>(itemIndex) < index + count) {
228                 NotifyDataChanged(index, child.second, false);
229                 nodeList_.emplace_back(child.first, child.second);
230                 iter = cachedItems_.erase(iter);
231             } else {
232                 iter++;
233             }
234         }
235         for (auto& [key, node] : expiringItem_) {
236             if (static_cast<size_t>(node.first) >= index && static_cast<size_t>(node.first) < index + count) {
237                 node.first = -1;
238             }
239         }
240         return nodeList_;
241     }
242 
OnDataMoveToNewPlace(size_t from,size_t to)243     void LazyForEachBuilder::OnDataMoveToNewPlace(size_t from, size_t to)
244     {
245         if (from == to) {
246             return;
247         }
248         decltype(cachedItems_) temp(std::move(cachedItems_));
249         for (const auto& [itemIndex, child] : temp) {
250             auto position = static_cast<size_t>(itemIndex);
251             if (position > from && position <= to && position >= 1) { // from < position <= to
252                 cachedItems_.emplace(position - 1, child);
253             } else if (position >= to && position < from) { // to <= position < from
254                 cachedItems_.emplace(position + 1, child);
255             } else if (position == from) {
256                 cachedItems_.emplace(to, child);
257             } else {
258                 cachedItems_.emplace(itemIndex, child);
259             }
260         }
261 
262         for (const auto& [key, child] : expiringItem_) {
263             auto position = static_cast<size_t>(child.first);
264             if (position > from && position <= to && position >= 1) { // from < position <= to
265                 expiringItem_[key] = LazyForEachCacheChild(position - 1, std::move(child.second));
266             } else if (position >= to && position < from) { // to <= position < from
267                 expiringItem_[key] = LazyForEachCacheChild(position + 1, std::move(child.second));
268             } else if (position == from) {
269                 expiringItem_[key] = LazyForEachCacheChild(to, std::move(child.second));
270             }
271         }
272     }
273 
OnDataMoved(size_t from,size_t to)274     bool LazyForEachBuilder::OnDataMoved(size_t from, size_t to)
275     {
276         if (from == to) {
277             return false;
278         }
279         auto fromIter = cachedItems_.find(from);
280         auto toIter = cachedItems_.find(to);
281         if (fromIter != cachedItems_.end() && toIter != cachedItems_.end()) {
282             std::swap(fromIter->second, toIter->second);
283         } else if (fromIter != cachedItems_.end()) {
284             expiringItem_[fromIter->second.first] = LazyForEachCacheChild(to, std::move(fromIter->second.second));
285             cachedItems_.erase(fromIter);
286         } else if (toIter != cachedItems_.end()) {
287             expiringItem_[toIter->second.first] = LazyForEachCacheChild(from, std::move(toIter->second.second));
288             cachedItems_.erase(toIter);
289         }
290         return true;
291     }
292 
GetAllItems(std::vector<UINode * > & items)293     void LazyForEachBuilder::GetAllItems(std::vector<UINode*>& items)
294     {
295         for (const auto& item : cachedItems_) {
296             items.emplace_back(RawPtr(item.second.second));
297         }
298         for (const auto& item : expiringItem_) {
299             items.emplace_back(RawPtr(item.second.second));
300         }
301         for (const auto& item : nodeList_) {
302             items.emplace_back(RawPtr(item.second));
303         }
304     }
305 
Transit(std::list<std::pair<std::string,RefPtr<UINode>>> & childList)306     void LazyForEachBuilder::Transit(std::list<std::pair<std::string, RefPtr<UINode>>>& childList)
307     {
308         if (needTransition) {
309             for (auto& [key, node] : expiringItem_) {
310                 if (!node.second) {
311                     continue;
312                 }
313                 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
314                 if (frameNode && frameNode->IsOnMainTree()) {
315                     childList.emplace_back(key, node.second);
316                 }
317             }
318             needTransition = false;
319         }
320     }
321 
GetItems(std::list<std::pair<std::string,RefPtr<UINode>>> & childList)322     std::map<int32_t, LazyForEachChild>& LazyForEachBuilder::GetItems(
323         std::list<std::pair<std::string, RefPtr<UINode>>>& childList)
324     {
325         startIndex_ = -1;
326         endIndex_ = -1;
327         int32_t lastIndex = -1;
328         bool isCertained = false;
329 
330         decltype(cachedItems_) items(std::move(cachedItems_));
331 
332         for (auto& [index, node] : items) {
333             if (!node.second) {
334                 cachedItems_.try_emplace(index, std::move(node));
335                 continue;
336             }
337 
338             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
339             if (frameNode && !frameNode->IsActive()) {
340                 ACE_SYNTAX_SCOPED_TRACE("LazyForEach not active index[%d]", index);
341                 frameNode->SetJSViewActive(false, true);
342                 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
343                 continue;
344             }
345             cachedItems_.try_emplace(index, std::move(node));
346             if (startIndex_ == -1) {
347                 startIndex_ = index;
348             }
349             if (isLoop_) {
350                 if (isCertained) {
351                     continue;
352                 }
353                 if (lastIndex > -1 && index - lastIndex > 1) {
354                     startIndex_ = index;
355                     endIndex_ = lastIndex;
356                     isCertained = true;
357                 } else {
358                     endIndex_ = std::max(endIndex_, index);
359                 }
360             } else {
361                 endIndex_ = std::max(endIndex_, index);
362             }
363             lastIndex = index;
364         }
365 
366         Transit(childList);
367 
368         return cachedItems_;
369     }
370 
GetTotalCountOfOriginalDataset()371     int32_t LazyForEachBuilder::GetTotalCountOfOriginalDataset()
372     {
373         int32_t totalCount = GetTotalCount();
374         int32_t totalCountOfOriginalDataset = historicalTotalCount_;
375         UpdateHistoricalTotalCount(totalCount);
376         return totalCountOfOriginalDataset;
377     }
378 
OnDatasetChange(std::list<V2::Operation> DataOperations)379     std::pair<int32_t, std::list<std::pair<std::string, RefPtr<UINode>>>> LazyForEachBuilder::OnDatasetChange(
380         std::list<V2::Operation> DataOperations)
381     {
382         totalCountOfOriginalDataset_ = GetTotalCountOfOriginalDataset();
383         int32_t initialIndex = totalCountOfOriginalDataset_;
384         std::map<int32_t, LazyForEachChild> expiringTempItem_;
385         std::list<std::string> expiringKeys;
386         for (auto& [key, cacheChild] : expiringItem_) {
387             if (cacheChild.first > -1) {
388                 expiringTempItem_.try_emplace(cacheChild.first, LazyForEachChild(key, cacheChild.second));
389                 expiringKeys.emplace_back(key);
390             }
391         }
392         for (auto& key : expiringKeys) {
393             expiringItem_.erase(key);
394         }
395         decltype(expiringTempItem_) expiringTemp(std::move(expiringTempItem_));
396         for (auto operation : DataOperations) {
397             bool isReload = ClassifyOperation(operation, initialIndex, cachedItems_, expiringTemp);
398             if (isReload) {
399                 initialIndex = 0;
400                 return std::pair(initialIndex, nodeList_);
401             }
402         }
403         decltype(cachedItems_) cachedTemp(std::move(cachedItems_));
404         std::map<int32_t, int32_t> indexChangedMap;
405         CollectIndexChangedCount(indexChangedMap);
406         RepairDatasetItems(cachedTemp, cachedItems_, indexChangedMap);
407         RepairDatasetItems(expiringTemp, expiringTempItem_, indexChangedMap);
408         for (auto& [index, node] : expiringTempItem_) {
409             expiringItem_.emplace(node.first, LazyForEachCacheChild(index, node.second));
410         }
411         operationList_.clear();
412         return std::pair(initialIndex, nodeList_);
413     }
414 
RepairDatasetItems(std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTempItem_,std::map<int32_t,int32_t> & indexChangedMap)415     void LazyForEachBuilder::RepairDatasetItems(std::map<int32_t, LazyForEachChild>& cachedTemp,
416         std::map<int32_t, LazyForEachChild>& expiringTempItem_, std::map<int32_t, int32_t>& indexChangedMap)
417     {
418         int32_t changedIndex = 0;
419         for (auto& [index, child] : cachedTemp) {
420             auto iter = indexChangedMap.find(index);
421             if (iter == indexChangedMap.end()) {
422                 if (!indexChangedMap.empty()) {
423                     iter--;
424                     if (iter->first < index) {
425                         changedIndex = iter->second;
426                     }
427                 }
428             } else {
429                 changedIndex = iter->second;
430             }
431             if (operationList_.find(index) == operationList_.end()) {
432                 expiringTempItem_.try_emplace(index + changedIndex, child);
433                 continue;
434             }
435             if (!indexChangedMap.empty()) {
436                 changedIndex = iter->second;
437             }
438             auto info = operationList_.find(index)->second;
439             if (info.isDeleting) {
440                 nodeList_.emplace_back(child.first, child.second);
441             } else if (info.isChanged) {
442                 expiringTempItem_.try_emplace(index + changedIndex, LazyForEachChild(info.key, nullptr));
443             } else if (!info.extraKey.empty()) {
444                 expiringTempItem_.try_emplace(index + changedIndex, child);
445                 int32_t preChangedIndex = 0;
446                 auto preIter = indexChangedMap.find(index - 1);
447                 if (preIter != indexChangedMap.end()) {
448                     preChangedIndex = preIter->second;
449                 }
450                 for (int32_t i = 0; i < static_cast<int32_t>(info.extraKey.size()); i++) {
451                     expiringTempItem_.try_emplace(
452                         index + preChangedIndex + i, LazyForEachChild(info.extraKey[i], nullptr));
453                 }
454             } else if (info.moveIn || info.isExchange) {
455                 RepairMoveOrExchange(expiringTempItem_, info, child, index, changedIndex);
456             } else {
457                 expiringTempItem_.try_emplace(index + changedIndex, child);
458             }
459         }
460     }
461 
RepairMoveOrExchange(std::map<int32_t,LazyForEachChild> & expiringTempItem_,OperationInfo & info,LazyForEachChild & child,int32_t index,int32_t changedIndex)462     void LazyForEachBuilder::RepairMoveOrExchange(std::map<int32_t, LazyForEachChild>& expiringTempItem_,
463         OperationInfo& info, LazyForEachChild& child, int32_t index, int32_t changedIndex)
464     {
465         if (info.isExchange) {
466             // if the child will be exchanged with a null node, this child should be deleted
467             if (info.node == nullptr && child.second != nullptr) {
468                 nodeList_.emplace_back(child.first, child.second);
469             }
470             // null node should not be put in expiringTempItem_ then expiringTempItem_
471             if (info.node != nullptr) {
472                 expiringTempItem_.try_emplace(index + changedIndex, LazyForEachChild(info.key, info.node));
473             }
474             return;
475         }
476         if (info.moveIn) {
477             int32_t fromIndex = index + changedIndex - 1;
478             int32_t toIndex = index + changedIndex;
479             if (info.fromDiffTo > 0) {
480                 fromIndex = index + changedIndex;
481                 toIndex = index + changedIndex - 1;
482             }
483             expiringTempItem_.try_emplace(toIndex, LazyForEachChild(info.key, info.node));
484             expiringTempItem_.try_emplace(fromIndex, child);
485         }
486     }
487 
CollectIndexChangedCount(std::map<int32_t,int32_t> & indexChangedMap)488     void LazyForEachBuilder::CollectIndexChangedCount(std::map<int32_t, int32_t>& indexChangedMap)
489     {
490         int32_t changedIndex = 0;
491         for (auto& [index, operationInfo] : operationList_) {
492             if (indexChangedMap.size() >= static_cast<size_t>(1)) {
493                 for (int32_t i = indexChangedMap.rbegin()->first + 1; i < index; i++) {
494                     indexChangedMap.try_emplace(i, changedIndex);
495                 }
496             }
497             operationInfo.changeCount += changedIndex;
498             changedIndex = operationInfo.changeCount;
499             indexChangedMap.try_emplace(index, changedIndex);
500         }
501     }
502 
ClassifyOperation(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)503     bool LazyForEachBuilder::ClassifyOperation(V2::Operation& operation, int32_t& initialIndex,
504         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
505     {
506         switch (operationTypeMap[operation.type]) {
507             case OP::ADD:
508                 OperateAdd(operation, initialIndex);
509                 break;
510             case OP::DEL:
511                 OperateDelete(operation, initialIndex);
512                 break;
513             case OP::CHANGE:
514                 OperateChange(operation, initialIndex, cachedTemp, expiringTemp);
515                 break;
516             case OP::MOVE:
517                 OperateMove(operation, initialIndex, cachedTemp, expiringTemp);
518                 break;
519             case OP::EXCHANGE:
520                 OperateExchange(operation, initialIndex, cachedTemp, expiringTemp);
521                 break;
522             case OP::RELOAD:
523                 OperateReload(expiringTemp);
524                 return true;
525         }
526         return false;
527     }
528 
ValidateIndex(int32_t index,const std::string & type)529     bool LazyForEachBuilder::ValidateIndex(int32_t index, const std::string& type)
530     {
531         bool isValid = true;
532         if (operationTypeMap[type] == OP::ADD) {
533             // for add operation, the index can equal totalCountOfOriginalDataset_
534             isValid = index >= 0 && index <= totalCountOfOriginalDataset_;
535         } else {
536             isValid = index >= 0 && index < totalCountOfOriginalDataset_;
537         }
538         if (!isValid) {
539             TAG_LOGE(
540                 AceLogTag::ACE_LAZY_FOREACH, "%{public}s(%{public}d) Operation is out of range", type.c_str(), index);
541         }
542         return isValid;
543     }
544 
OperateAdd(V2::Operation & operation,int32_t & initialIndex)545     void LazyForEachBuilder::OperateAdd(V2::Operation& operation, int32_t& initialIndex)
546     {
547         OperationInfo itemInfo;
548         if (!ValidateIndex(operation.index, operation.type)) {
549             return;
550         }
551         auto indexExist = operationList_.find(operation.index);
552         if (indexExist == operationList_.end()) {
553             itemInfo.changeCount = operation.count;
554             if (!operation.key.empty()) {
555                 itemInfo.extraKey.push_back(operation.key);
556             } else if (operation.keyList.size() >= static_cast<size_t>(1)) {
557                 for (std::string key : operation.keyList) {
558                     itemInfo.extraKey.push_back(key);
559                 }
560             }
561             initialIndex = std::min(initialIndex, operation.index);
562             operationList_.try_emplace(operation.index, itemInfo);
563         } else {
564             ThrowRepeatOperationError(operation.index);
565         }
566     }
567 
OperateDelete(V2::Operation & operation,int32_t & initialIndex)568     void LazyForEachBuilder::OperateDelete(V2::Operation& operation, int32_t& initialIndex)
569     {
570         OperationInfo itemInfo;
571         if (!ValidateIndex(operation.index, operation.type)) {
572             return;
573         }
574         auto indexExist = operationList_.find(operation.index);
575         if (indexExist == operationList_.end()) {
576             itemInfo.changeCount = -operation.count;
577             itemInfo.isDeleting = true;
578             initialIndex = std::min(initialIndex, operation.index);
579             operationList_.try_emplace(operation.index, itemInfo);
580             for (int32_t i = operation.index + 1; i < operation.index + operation.count; i++) {
581                 OperationInfo extraInfo;
582                 if (operationList_.find(i) == operationList_.end()) {
583                     extraInfo.isDeleting = true;
584                     operationList_.try_emplace(i, extraInfo);
585                 } else {
586                     ThrowRepeatOperationError(i);
587                 }
588             }
589         } else {
590             ThrowRepeatOperationError(operation.index);
591         }
592     }
593 
OperateChange(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)594     void LazyForEachBuilder::OperateChange(V2::Operation& operation, int32_t& initialIndex,
595         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
596     {
597         OperationInfo itemInfo;
598         if (!ValidateIndex(operation.index, operation.type)) {
599             return;
600         }
601         auto indexExist = operationList_.find(operation.index);
602         if (indexExist == operationList_.end()) {
603             itemInfo.isChanged = true;
604             auto iter = cachedTemp.find(operation.index);
605             if (iter == cachedTemp.end()) {
606                 iter = expiringTemp.find(operation.index);
607             }
608             if (iter == expiringTemp.end()) {
609                 return;
610             }
611             if (!operation.key.empty()) {
612                 itemInfo.key = operation.key;
613             } else {
614                 itemInfo.key = iter->second.first;
615             }
616             initialIndex = std::min(initialIndex, operation.index);
617             operationList_.try_emplace(operation.index, itemInfo);
618         } else {
619             ThrowRepeatOperationError(operation.index);
620         }
621     }
622 
OperateMove(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)623     void LazyForEachBuilder::OperateMove(V2::Operation& operation, int32_t& initialIndex,
624         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
625     {
626         OperationInfo fromInfo;
627         OperationInfo toInfo;
628         if (!ValidateIndex(operation.coupleIndex.first, operation.type) ||
629             !ValidateIndex(operation.coupleIndex.second, operation.type)) {
630             return;
631         }
632         auto fromIndexExist = operationList_.find(operation.coupleIndex.first);
633         auto toIndexExist = operationList_.find(operation.coupleIndex.second);
634         if (fromIndexExist == operationList_.end()) {
635             fromInfo.changeCount = -1;
636             fromInfo.isDeleting = true;
637             initialIndex = std::min(initialIndex, operation.coupleIndex.first);
638             operationList_.try_emplace(operation.coupleIndex.first, fromInfo);
639         } else {
640             ThrowRepeatOperationError(operation.coupleIndex.first);
641         }
642         if (toIndexExist == operationList_.end()) {
643             toInfo.changeCount = 1;
644             auto iter = cachedTemp.find(operation.coupleIndex.first);
645             if (iter == cachedTemp.end()) {
646                 iter = expiringTemp.find(operation.coupleIndex.first);
647             }
648             if (iter == expiringTemp.end()) {
649                 return;
650             }
651             toInfo.node = iter->second.second;
652             toInfo.moveIn = true;
653             toInfo.fromDiffTo = operation.coupleIndex.first - operation.coupleIndex.second;
654             if (!operation.key.empty()) {
655                 toInfo.key = operation.key;
656             } else {
657                 toInfo.key = iter->second.first;
658             }
659             initialIndex = std::min(initialIndex, operation.coupleIndex.second);
660             operationList_.try_emplace(operation.coupleIndex.second, toInfo);
661         } else {
662             ThrowRepeatOperationError(operation.coupleIndex.second);
663         }
664     }
665 
OperateExchange(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)666     void LazyForEachBuilder::OperateExchange(V2::Operation& operation, int32_t& initialIndex,
667         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
668     {
669         OperationInfo startInfo;
670         OperationInfo endInfo;
671         if (!ValidateIndex(operation.coupleIndex.first, operation.type) ||
672             !ValidateIndex(operation.coupleIndex.second, operation.type)) {
673             return;
674         }
675         auto startIndexExist = operationList_.find(operation.coupleIndex.first);
676         auto endIndexExist = operationList_.find(operation.coupleIndex.second);
677         if (startIndexExist == operationList_.end()) {
678             auto iter = FindItem(operation.coupleIndex.first, cachedTemp, expiringTemp);
679             // if item can't be find in cachedItems_ nor expiringItem_, set UI node to null
680             if (iter == expiringTemp.end()) {
681                 startInfo.node = nullptr;
682             } else {
683                 startInfo.node = iter->second.second;
684                 if (!operation.coupleKey.first.empty()) {
685                     startInfo.key = operation.coupleKey.first;
686                 } else {
687                     startInfo.key = iter->second.first;
688                 }
689             }
690             startInfo.isExchange = true;
691             initialIndex = std::min(initialIndex, operation.coupleIndex.second);
692             operationList_.try_emplace(operation.coupleIndex.second, startInfo);
693         } else {
694             ThrowRepeatOperationError(operation.coupleIndex.first);
695         }
696         if (endIndexExist == operationList_.end()) {
697             auto iter = FindItem(operation.coupleIndex.second, cachedTemp, expiringTemp);
698             // if item can't be find in cachedItems_ nor expiringItem_, set UI node to null
699             if (iter == expiringTemp.end()) {
700                 endInfo.node = nullptr;
701             } else {
702                 endInfo.node = iter->second.second;
703                 if (!operation.coupleKey.second.empty()) {
704                     endInfo.key = operation.coupleKey.second;
705                 } else {
706                     endInfo.key = iter->second.first;
707                 }
708             }
709             endInfo.isExchange = true;
710             initialIndex = std::min(initialIndex, operation.coupleIndex.first);
711             operationList_.try_emplace(operation.coupleIndex.first, endInfo);
712         } else {
713             ThrowRepeatOperationError(operation.coupleIndex.second);
714         }
715     }
716 
FindItem(int32_t index,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)717     std::map<int32_t, LazyForEachChild>::iterator LazyForEachBuilder::FindItem(int32_t index,
718         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
719     {
720         auto iterOfCached = cachedTemp.find(index);
721         auto iterOfExpiring = expiringTemp.find(index);
722         // if UI node can't be find in cachedTemp, find it in expiringTemp
723         if (iterOfCached == cachedTemp.end() || iterOfCached->second.second == nullptr) {
724             return iterOfExpiring;
725         } else {
726             return iterOfCached;
727         }
728     }
729 
OperateReload(std::map<int32_t,LazyForEachChild> & expiringTemp)730     void LazyForEachBuilder::OperateReload(std::map<int32_t, LazyForEachChild>& expiringTemp)
731     {
732         for (auto& [index, node] : expiringTemp) {
733             expiringItem_.emplace(node.first, LazyForEachCacheChild(index, node.second));
734         }
735         operationList_.clear();
736         OnDataReloaded();
737     }
738 
ThrowRepeatOperationError(int32_t index)739     void LazyForEachBuilder::ThrowRepeatOperationError(int32_t index)
740     {
741         TAG_LOGE(AceLogTag::ACE_LAZY_FOREACH, "Repeat Operation for index: %{public}d", index);
742     }
743 
RecycleChildByIndex(int32_t index)744     void LazyForEachBuilder::RecycleChildByIndex(int32_t index)
745     {
746         auto iter = cachedItems_.find(index);
747         if (iter != cachedItems_.end()) {
748             if (!iter->second.second) {
749                 return;
750             }
751             auto dummyNode = AceType::DynamicCast<RecycleDummyNode>(iter->second.second);
752             if (!dummyNode) {
753                 return;
754             }
755             auto keyIter = expiringItem_.find(iter->second.first);
756             if (keyIter != expiringItem_.end() && keyIter->second.second) {
757                 expiringItem_.erase(keyIter);
758             }
759             cachedItems_.erase(index);
760         }
761     }
762 
PreBuild(int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)763     bool LazyForEachBuilder::PreBuild(int64_t deadline, const std::optional<LayoutConstraintF>& itemConstraint,
764         bool canRunLongPredictTask)
765     {
766         ACE_SYNTAX_SCOPED_TRACE("expiringItem_ count:[%zu]", expiringItem_.size());
767         outOfBoundaryNodes_.clear();
768         if (itemConstraint && !canRunLongPredictTask) {
769             return false;
770         }
771         auto count = OnGetTotalCount();
772         std::unordered_map<std::string, LazyForEachCacheChild> cache;
773         std::set<int32_t> idleIndexes;
774         if (startIndex_ != -1 && endIndex_ != -1) {
775             CheckCacheIndex(idleIndexes, count);
776         }
777 
778         ProcessCachedIndex(cache, idleIndexes);
779 
780         bool result = true;
781         result = ProcessPreBuildingIndex(cache, deadline, itemConstraint, canRunLongPredictTask, idleIndexes);
782         if (!result) {
783             expiringItem_.swap(cache);
784             return result;
785         }
786 
787         for (auto index : idleIndexes) {
788             result = PreBuildByIndex(index, cache, deadline, itemConstraint, canRunLongPredictTask);
789             if (!result) {
790                 break;
791             }
792         }
793         expiringItem_.swap(cache);
794         return result;
795     }
796 
RecordOutOfBoundaryNodes(int32_t index)797     void LazyForEachBuilder::RecordOutOfBoundaryNodes(int32_t index)
798     {
799         outOfBoundaryNodes_.emplace_back(index);
800     }
801 
RecycleItemsOutOfBoundary()802     void LazyForEachBuilder::RecycleItemsOutOfBoundary()
803     {
804         for (const auto& i: outOfBoundaryNodes_) {
805             RecycleChildByIndex(i);
806         }
807         outOfBoundaryNodes_.clear();
808     }
809 
UpdateMoveFromTo(int32_t from,int32_t to)810     void LazyForEachBuilder::UpdateMoveFromTo(int32_t from, int32_t to)
811     {
812         if (moveFromTo_) {
813             moveFromTo_.value().second = to;
814         } else {
815             moveFromTo_ = { from, to };
816         }
817     }
818 
ResetMoveFromTo()819     void LazyForEachBuilder::ResetMoveFromTo()
820     {
821         moveFromTo_.reset();
822     }
823 
ConvertFromToIndex(int32_t index)824     int32_t LazyForEachBuilder::ConvertFromToIndex(int32_t index)
825     {
826         if (!moveFromTo_) {
827             return index;
828         }
829         if (moveFromTo_.value().second == index) {
830             return moveFromTo_.value().first;
831         }
832         if (moveFromTo_.value().first <= index && index < moveFromTo_.value().second) {
833             return index + 1;
834         }
835         if (moveFromTo_.value().second < index && index <= moveFromTo_.value().first) {
836             return index - 1;
837         }
838         return index;
839     }
840 
RemoveAllChild()841     void LazyForEachBuilder::RemoveAllChild()
842     {
843         ACE_SYNTAX_SCOPED_TRACE("LazyForEach RemoveAllChild");
844         for (auto& [index, node] : cachedItems_) {
845             if (!node.second) {
846                 continue;
847             }
848             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
849             if (frameNode) {
850                 frameNode->SetActive(false);
851             }
852             auto tempNode = node.second;
853             auto pair = expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
854             if (!pair.second) {
855                 TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Use repeat key for index: %{public}d", index);
856                 ProcessOffscreenNode(tempNode, true);
857             }
858         }
859     }
860 
SetActiveChildRange(int32_t start,int32_t end)861     bool LazyForEachBuilder::SetActiveChildRange(int32_t start, int32_t end)
862     {
863         ACE_SYNTAX_SCOPED_TRACE("LazyForEach active range start[%d], end[%d]", start, end);
864         int32_t count = GetTotalCount();
865         UpdateHistoricalTotalCount(count);
866         bool needBuild = false;
867         auto tempCachedItems = cachedItems_;
868         for (auto& [index, node] : tempCachedItems) {
869             bool isInRange = (index < count) && ((start <= end && start <= index && end >= index) ||
870                 (start > end && (index <= end || index >= start)));
871             if (!isInRange) {
872                 if (!node.second) {
873                     continue;
874                 }
875                 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
876                 if (frameNode) {
877                     frameNode->SetActive(false);
878                 }
879                 cachedItems_[index] = LazyForEachChild(node.first, nullptr);
880                 auto tempNode = node.second;
881                 auto pair = expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
882                 if (!pair.second) {
883                     TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Use repeat key for index: %{public}d", index);
884                     ProcessOffscreenNode(tempNode, true);
885                 }
886                 needBuild = true;
887                 continue;
888             }
889             if (node.second) {
890                 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
891                 if (frameNode) {
892                     frameNode->SetActive(true);
893                 }
894                 continue;
895             }
896             auto keyIter = expiringItem_.find(node.first);
897             if (keyIter != expiringItem_.end() && keyIter->second.second) {
898                 node.second = keyIter->second.second;
899                 expiringItem_.erase(keyIter);
900                 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
901                 if (frameNode) {
902                     frameNode->SetActive(true);
903                 }
904             }
905             needBuild = true;
906         }
907         return needBuild;
908     }
909 
GetChildIndex(const RefPtr<FrameNode> & targetNode)910     int32_t LazyForEachBuilder::GetChildIndex(const RefPtr<FrameNode>& targetNode)
911     {
912         for (auto& [index, node] : cachedItems_) {
913             if (node.second) {
914                 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
915                 if (frameNode == targetNode) {
916                     return index;
917                 }
918             }
919         }
920         for (auto& [key, node] : expiringItem_) {
921             if (!node.second) {
922                 continue;
923             }
924             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
925             if (frameNode && frameNode == targetNode) {
926                 return node.first;
927             }
928         }
929         return -1;
930     }
931 
CacheItem(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,const std::optional<LayoutConstraintF> & itemConstraint,int64_t deadline,bool & isTimeout)932     RefPtr<UINode> LazyForEachBuilder::CacheItem(int32_t index,
933         std::unordered_map<std::string, LazyForEachCacheChild>& cache,
934         const std::optional<LayoutConstraintF>& itemConstraint, int64_t deadline, bool& isTimeout)
935     {
936         ACE_SCOPED_TRACE("Builder:BuildLazyItem [%d]", index);
937         auto itemInfo = OnGetChildByIndex(ConvertFromToIndex(index), expiringItem_);
938         CHECK_NULL_RETURN(itemInfo.second, nullptr);
939         auto pair = cache.try_emplace(itemInfo.first, LazyForEachCacheChild(index, itemInfo.second));
940         auto context = itemInfo.second->GetContext();
941         CHECK_NULL_RETURN(context, itemInfo.second);
942         auto frameNode = AceType::DynamicCast<FrameNode>(itemInfo.second->GetFrameChildByIndex(0, false, true));
943         context->SetPredictNode(frameNode);
944         if (!itemInfo.second->RenderCustomChild(deadline)) {
945             isTimeout = true;
946             context->ResetPredictNode();
947             return itemInfo.second;
948         }
949         if (pair.second) {
950             ProcessOffscreenNode(itemInfo.second, false);
951         } else {
952             TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Use repeat key for index: %{public}d", index);
953         }
954 
955         itemInfo.second->Build(nullptr);
956         context->ResetPredictNode();
957         itemInfo.second->SetJSViewActive(false, true);
958         cachedItems_[index] = LazyForEachChild(itemInfo.first, nullptr);
959         return itemInfo.second;
960     }
961 
CheckCacheIndex(std::set<int32_t> & idleIndexes,int32_t count)962     void LazyForEachBuilder::CheckCacheIndex(std::set<int32_t>& idleIndexes, int32_t count)
963     {
964         if (count == 0) {
965             return;
966         }
967         for (int32_t i = 1; i <= cacheCount_ - endShowCached_; i++) {
968             if (isLoop_) {
969                 if ((startIndex_ <= endIndex_ && endIndex_ + i < count) ||
970                     startIndex_ > endIndex_ + i) {
971                     idleIndexes.emplace(endIndex_ + i);
972                 } else if ((endIndex_ + i) % count < startIndex_) {
973                     idleIndexes.emplace((endIndex_ + i) % count);
974                 }
975             } else {
976                 if (endIndex_ + i < count) {
977                     idleIndexes.emplace(endIndex_ + i);
978                 }
979             }
980         }
981         for (int32_t i = 1; i <= cacheCount_ - startShowCached_; i++) {
982             if (isLoop_) {
983                 if ((startIndex_ <= endIndex_ && startIndex_ >= i) ||
984                     startIndex_ > endIndex_ + i) {
985                     idleIndexes.emplace(startIndex_ - i);
986                 } else if ((startIndex_ - i + count) % count > endIndex_) {
987                     idleIndexes.emplace((startIndex_ - i + count) % count);
988                 }
989             } else {
990                 if (startIndex_ >= i) {
991                     idleIndexes.emplace(startIndex_ - i);
992                 }
993             }
994         }
995     }
996 
PreBuildByIndex(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)997     bool LazyForEachBuilder::PreBuildByIndex(int32_t index,
998         std::unordered_map<std::string, LazyForEachCacheChild>& cache, int64_t deadline,
999         const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask)
1000     {
1001         if (!enablePreBuild_ || GetSysTimestamp() > deadline) {
1002             if (DeleteExpiringItemImmediately()) {
1003                 return false;
1004             }
1005             for (const auto& [key, node] : expiringItem_) {
1006                 if (node.first == -1) {
1007                     cache.try_emplace(key, node);
1008                 }
1009             }
1010             return false;
1011         }
1012         bool isTimeout = false;
1013         preBuildingIndex_ = -1;
1014         auto uiNode = CacheItem(index, cache, itemConstraint, deadline, isTimeout);
1015         if (isTimeout) {
1016             preBuildingIndex_ = index;
1017             return false;
1018         }
1019         if (!canRunLongPredictTask && itemConstraint) {
1020             return false;
1021         }
1022         if (canRunLongPredictTask && uiNode && itemConstraint) {
1023             RefPtr<FrameNode> frameNode = DynamicCast<FrameNode>(uiNode);
1024             while (!frameNode) {
1025                 auto tempNode = uiNode;
1026                 uiNode = tempNode->GetFirstChild();
1027                 if (!uiNode) {
1028                     break;
1029                 }
1030                 frameNode = DynamicCast<FrameNode>(uiNode);
1031             }
1032             if (frameNode) {
1033                 frameNode->GetGeometryNode()->SetParentLayoutConstraint(itemConstraint.value());
1034                 FrameNode::ProcessOffscreenNode(frameNode);
1035             }
1036         }
1037         return true;
1038     }
1039 
ProcessCachedIndex(std::unordered_map<std::string,LazyForEachCacheChild> & cache,std::set<int32_t> & idleIndexes)1040     void LazyForEachBuilder::ProcessCachedIndex(std::unordered_map<std::string, LazyForEachCacheChild>& cache,
1041         std::set<int32_t>& idleIndexes)
1042     {
1043         auto expiringIter = expiringItem_.begin();
1044         while (expiringIter != expiringItem_.end()) {
1045             const auto& key = expiringIter->first;
1046             const auto& node = expiringIter->second;
1047             auto iter = idleIndexes.find(node.first);
1048             if (iter != idleIndexes.end() && node.second) {
1049                 LoadCacheByIndex(cache, idleIndexes, node, key, iter, expiringIter);
1050             } else {
1051                 LoadCacheByKey(cache, idleIndexes, node, key, expiringIter);
1052             }
1053         }
1054     }
1055 
ProcessOffscreenNode(RefPtr<UINode> uiNode,bool remove)1056     void LazyForEachBuilder::ProcessOffscreenNode(RefPtr<UINode> uiNode, bool remove)
1057     {
1058         if (uiNode) {
1059             auto frameNode = DynamicCast<FrameNode>(uiNode);
1060             while (!frameNode) {
1061                 auto tempNode = uiNode;
1062                 uiNode = tempNode->GetFirstChild();
1063                 if (!uiNode) {
1064                     break;
1065                 }
1066                 frameNode = DynamicCast<FrameNode>(uiNode);
1067             }
1068             if (frameNode) {
1069                 if (!remove) {
1070                     Inspector::AddOffscreenNode(frameNode);
1071                 } else {
1072                     Inspector::RemoveOffscreenNode(frameNode);
1073                 }
1074             }
1075         }
1076     }
1077 
GetAllChildren()1078     const std::map<int32_t, LazyForEachChild>& LazyForEachBuilder::GetAllChildren()
1079     {
1080         if (!cachedItems_.empty()) {
1081             startIndex_ = cachedItems_.begin()->first;
1082             endIndex_ = cachedItems_.rbegin()->first;
1083         }
1084         if (isLoop_ && !cachedItems_.empty()) {
1085             int32_t lastIndex = -1;
1086             for (auto& [index, node] : cachedItems_) {
1087                 if (lastIndex > -1 && index - lastIndex > 1) {
1088                     startIndex_ = index;
1089                     endIndex_ = lastIndex;
1090                     break;
1091                 }
1092             }
1093         }
1094         return cachedItems_;
1095     }
1096 
1097     /**
1098      * Traverse nodes in cachedItems_, expiringItem_ and nodeList_, set the MeasureAnyway and Rerenderable properties
1099      * of all children the same value as LazyForEach node, and call NotifyColorModeChange.
1100      * When MeasureAnyway is true, perform measure and layout, force an update.
1101      * For cachedItems_, nodes are active and will measure and layout anyway, so skip SetMeasureAnyway.
1102      */
NotifyColorModeChange(uint32_t colorMode,bool rerenderable)1103     void LazyForEachBuilder::NotifyColorModeChange(uint32_t colorMode, bool rerenderable)
1104     {
1105         for (const auto& node : cachedItems_) {
1106             if (node.second.second == nullptr) {
1107                 continue;
1108             }
1109             node.second.second->SetRerenderable(rerenderable);
1110             node.second.second->NotifyColorModeChange(colorMode);
1111         }
1112         for (const auto& node : expiringItem_) {
1113             if (node.second.second == nullptr) {
1114                 continue;
1115             }
1116             node.second.second->SetMeasureAnyway(rerenderable);
1117             node.second.second->SetRerenderable(rerenderable);
1118             node.second.second->NotifyColorModeChange(colorMode);
1119         }
1120         for (const auto& node : nodeList_) {
1121             if (node.second == nullptr) {
1122                 continue;
1123             }
1124             node.second->SetMeasureAnyway(rerenderable);
1125             node.second->SetRerenderable(rerenderable);
1126             node.second->NotifyColorModeChange(colorMode);
1127         }
1128     }
1129 
SetJSViewActive(bool active)1130     void LazyForEachBuilder::SetJSViewActive(bool active)
1131     {
1132         for (const auto& node : cachedItems_) {
1133             if (node.second.second == nullptr) {
1134                 continue;
1135             }
1136             node.second.second->SetJSViewActive(active, true);
1137         }
1138         for (const auto& node : expiringItem_) {
1139             if (node.second.second == nullptr) {
1140                 continue;
1141             }
1142             node.second.second->SetJSViewActive(active, true);
1143         }
1144     }
1145 
PaintDebugBoundaryTreeAll(bool flag)1146     void LazyForEachBuilder::PaintDebugBoundaryTreeAll(bool flag)
1147     {
1148         for (const auto& node : cachedItems_) {
1149             if (node.second.second == nullptr) {
1150                 continue;
1151             }
1152             node.second.second->PaintDebugBoundaryTreeAll(flag);
1153         }
1154         for (const auto& node : expiringItem_) {
1155             if (node.second.second == nullptr) {
1156                 continue;
1157             }
1158             node.second.second->PaintDebugBoundaryTreeAll(flag);
1159         }
1160     }
SetDestroying(bool isDestroying,bool cleanStatus)1161     void LazyForEachBuilder::SetDestroying(bool isDestroying, bool cleanStatus)
1162     {
1163         for (const auto& node : cachedItems_) {
1164             if (node.second.second == nullptr) {
1165                 continue;
1166             }
1167             if (node.second.second->IsReusableNode()) {
1168                 node.second.second->SetDestroying(isDestroying, false);
1169             } else {
1170                 node.second.second->SetDestroying(isDestroying, cleanStatus);
1171             }
1172         }
1173 
1174         for (const auto& node : expiringItem_) {
1175             if (node.second.second == nullptr) {
1176                 continue;
1177             }
1178             if (node.second.second->IsReusableNode()) {
1179                 node.second.second->SetDestroying(isDestroying, false);
1180             } else {
1181                 node.second.second->SetDestroying(isDestroying, cleanStatus);
1182             }
1183         }
1184     }
1185 }
1186