• 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(ConvertFormToIndex(index), cachedItems_, expiringItem_);
45             } else {
46                 itemInfo = OnGetChildByIndex(ConvertFormToIndex(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         int32_t startIndex = -1;
326         int32_t 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         if (startIndex != -1 && endIndex != -1) {
369             startIndex_ = startIndex;
370             endIndex_ = endIndex;
371         }
372 
373         return cachedItems_;
374     }
375 
376     /**
377      * Application code normarlly manipulate datasource first then call OnDatasetChange. But there are cases that they
378      * call OnDatasetChange first, then manipulate datasource. So the return of function "GetTotalCount" can both be
379      * total count of current datasource(manipulate datasource first) and original datasource(call OnDatasetChange
380      * first). We need to maintain historicalTotalCount_ to get total count of original datasource.
381      */
GetTotalCountOfOriginalDataset()382     int32_t LazyForEachBuilder::GetTotalCountOfOriginalDataset()
383     {
384         int32_t totalCount = GetTotalCount();
385         int32_t totalCountOfOriginalDataset = historicalTotalCount_;
386         UpdateHistoricalTotalCount(totalCount);
387         return totalCountOfOriginalDataset;
388     }
389 
OnDatasetChange(std::list<V2::Operation> DataOperations)390     std::pair<int32_t, std::list<std::pair<std::string, RefPtr<UINode>>>> LazyForEachBuilder::OnDatasetChange(
391         std::list<V2::Operation> DataOperations)
392     {
393         totalCountOfOriginalDataset_ = GetTotalCountOfOriginalDataset();
394         int32_t initialIndex = totalCountOfOriginalDataset_;
395         std::map<int32_t, LazyForEachChild> expiringTempItem_;
396         std::list<std::string> expiringKeys;
397         for (auto& [key, cacheChild] : expiringItem_) {
398             if (cacheChild.first > -1) {
399                 expiringTempItem_.try_emplace(cacheChild.first, LazyForEachChild(key, cacheChild.second));
400                 expiringKeys.emplace_back(key);
401             }
402         }
403         for (auto& key : expiringKeys) {
404             expiringItem_.erase(key);
405         }
406         decltype(expiringTempItem_) expiringTemp(std::move(expiringTempItem_));
407         for (auto operation : DataOperations) {
408             bool isReload = ClassifyOperation(operation, initialIndex, cachedItems_, expiringTemp);
409             if (isReload) {
410                 initialIndex = 0;
411                 return std::pair(initialIndex, nodeList_);
412             }
413         }
414         decltype(cachedItems_) cachedTemp(std::move(cachedItems_));
415         std::map<int32_t, int32_t> indexChangedMap;
416         CollectIndexChangedCount(indexChangedMap);
417         RepairDatasetItems(cachedTemp, cachedItems_, indexChangedMap);
418         RepairDatasetItems(expiringTemp, expiringTempItem_, indexChangedMap);
419         for (auto& [index, node] : expiringTempItem_) {
420             if (node.second) {
421                 expiringItem_.emplace(node.first, LazyForEachCacheChild(index, node.second));
422             }
423         }
424         operationList_.clear();
425         return std::pair(initialIndex, nodeList_);
426     }
427 
RepairDatasetItems(std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTempItem_,std::map<int32_t,int32_t> & indexChangedMap)428     void LazyForEachBuilder::RepairDatasetItems(std::map<int32_t, LazyForEachChild>& cachedTemp,
429         std::map<int32_t, LazyForEachChild>& expiringTempItem_, std::map<int32_t, int32_t>& indexChangedMap)
430     {
431         int32_t changedIndex = 0;
432         for (auto& [index, child] : cachedTemp) {
433             auto iter = indexChangedMap.find(index);
434             if (iter == indexChangedMap.end()) {
435                 if (!indexChangedMap.empty()) {
436                     iter--;
437                     if (iter->first < index) {
438                         changedIndex = iter->second;
439                     }
440                 }
441             } else {
442                 changedIndex = iter->second;
443             }
444             if (operationList_.find(index) == operationList_.end()) {
445                 expiringTempItem_.try_emplace(index + changedIndex, child);
446                 continue;
447             }
448             if (!indexChangedMap.empty()) {
449                 changedIndex = iter->second;
450             }
451             auto info = operationList_.find(index)->second;
452             if (info.isDeleting) {
453                 nodeList_.emplace_back(child.first, child.second);
454             } else if (info.isChanged) {
455                 expiringTempItem_.try_emplace(index + changedIndex, LazyForEachChild(info.key, nullptr));
456             } else if (!info.extraKey.empty()) {
457                 expiringTempItem_.try_emplace(index + changedIndex, child);
458                 int32_t preChangedIndex = 0;
459                 auto preIter = indexChangedMap.find(index - 1);
460                 if (preIter != indexChangedMap.end()) {
461                     preChangedIndex = preIter->second;
462                 }
463                 for (int32_t i = 0; i < static_cast<int32_t>(info.extraKey.size()); i++) {
464                     expiringTempItem_.try_emplace(
465                         index + preChangedIndex + i, LazyForEachChild(info.extraKey[i], nullptr));
466                 }
467             } else if (info.moveIn || info.isExchange) {
468                 RepairMoveOrExchange(expiringTempItem_, info, child, index, changedIndex);
469             } else {
470                 expiringTempItem_.try_emplace(index + changedIndex, child);
471             }
472         }
473     }
474 
RepairMoveOrExchange(std::map<int32_t,LazyForEachChild> & expiringTempItem_,OperationInfo & info,LazyForEachChild & child,int32_t index,int32_t changedIndex)475     void LazyForEachBuilder::RepairMoveOrExchange(std::map<int32_t, LazyForEachChild>& expiringTempItem_,
476         OperationInfo& info, LazyForEachChild& child, int32_t index, int32_t changedIndex)
477     {
478         if (info.isExchange) {
479             // if the child will be exchanged with a null node, this child should be deleted
480             if (info.node == nullptr && child.second != nullptr) {
481                 nodeList_.emplace_back(child.first, child.second);
482             }
483             // null node should not be put in expiringTempItem_ then expiringTempItem_
484             if (info.node != nullptr) {
485                 expiringTempItem_.try_emplace(index + changedIndex, LazyForEachChild(info.key, info.node));
486             }
487             return;
488         }
489         if (info.moveIn) {
490             int32_t fromIndex = index + changedIndex - 1;
491             int32_t toIndex = index + changedIndex;
492             if (info.fromDiffTo > 0) {
493                 fromIndex = index + changedIndex;
494                 toIndex = index + changedIndex - 1;
495             }
496             expiringTempItem_.try_emplace(toIndex, LazyForEachChild(info.key, info.node));
497             expiringTempItem_.try_emplace(fromIndex, child);
498         }
499     }
500 
CollectIndexChangedCount(std::map<int32_t,int32_t> & indexChangedMap)501     void LazyForEachBuilder::CollectIndexChangedCount(std::map<int32_t, int32_t>& indexChangedMap)
502     {
503         int32_t changedIndex = 0;
504         for (auto& [index, operationInfo] : operationList_) {
505             if (indexChangedMap.size() >= static_cast<size_t>(1)) {
506                 for (int32_t i = indexChangedMap.rbegin()->first + 1; i < index; i++) {
507                     indexChangedMap.try_emplace(i, changedIndex);
508                 }
509             }
510             operationInfo.changeCount += changedIndex;
511             changedIndex = operationInfo.changeCount;
512             indexChangedMap.try_emplace(index, changedIndex);
513         }
514     }
515 
ClassifyOperation(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)516     bool LazyForEachBuilder::ClassifyOperation(V2::Operation& operation, int32_t& initialIndex,
517         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
518     {
519         switch (operationTypeMap[operation.type]) {
520             case OP::ADD:
521                 OperateAdd(operation, initialIndex);
522                 break;
523             case OP::DEL:
524                 OperateDelete(operation, initialIndex);
525                 break;
526             case OP::CHANGE:
527                 OperateChange(operation, initialIndex, cachedTemp, expiringTemp);
528                 break;
529             case OP::MOVE:
530                 OperateMove(operation, initialIndex, cachedTemp, expiringTemp);
531                 break;
532             case OP::EXCHANGE:
533                 OperateExchange(operation, initialIndex, cachedTemp, expiringTemp);
534                 break;
535             case OP::RELOAD:
536                 OperateReload(expiringTemp);
537                 return true;
538         }
539         return false;
540     }
541 
ValidateIndex(int32_t index,const std::string & type)542     bool LazyForEachBuilder::ValidateIndex(int32_t index, const std::string& type)
543     {
544         bool isValid = true;
545         if (operationTypeMap[type] == OP::ADD) {
546             // for add operation, the index can equal totalCountOfOriginalDataset_
547             isValid = index >= 0 && index <= totalCountOfOriginalDataset_;
548         } else {
549             isValid = index >= 0 && index < totalCountOfOriginalDataset_;
550         }
551         if (!isValid) {
552             TAG_LOGE(
553                 AceLogTag::ACE_LAZY_FOREACH, "%{public}s(%{public}d) Operation is out of range", type.c_str(), index);
554         }
555         return isValid;
556     }
557 
OperateAdd(V2::Operation & operation,int32_t & initialIndex)558     void LazyForEachBuilder::OperateAdd(V2::Operation& operation, int32_t& initialIndex)
559     {
560         OperationInfo itemInfo;
561         if (!ValidateIndex(operation.index, operation.type)) {
562             return;
563         }
564         auto indexExist = operationList_.find(operation.index);
565         if (indexExist == operationList_.end()) {
566             itemInfo.changeCount = operation.count;
567             if (!operation.key.empty()) {
568                 itemInfo.extraKey.push_back(operation.key);
569             } else if (operation.keyList.size() >= static_cast<size_t>(1)) {
570                 for (std::string key : operation.keyList) {
571                     itemInfo.extraKey.push_back(key);
572                 }
573             }
574             initialIndex = std::min(initialIndex, operation.index);
575             operationList_.try_emplace(operation.index, itemInfo);
576         } else {
577             ThrowRepeatOperationError(operation.index);
578         }
579     }
580 
OperateDelete(V2::Operation & operation,int32_t & initialIndex)581     void LazyForEachBuilder::OperateDelete(V2::Operation& operation, int32_t& initialIndex)
582     {
583         OperationInfo itemInfo;
584         if (!ValidateIndex(operation.index, operation.type)) {
585             return;
586         }
587         auto indexExist = operationList_.find(operation.index);
588         if (indexExist == operationList_.end()) {
589             itemInfo.changeCount = -operation.count;
590             itemInfo.isDeleting = true;
591             initialIndex = std::min(initialIndex, operation.index);
592             operationList_.try_emplace(operation.index, itemInfo);
593             for (int32_t i = operation.index + 1; i < operation.index + operation.count; i++) {
594                 OperationInfo extraInfo;
595                 if (operationList_.find(i) == operationList_.end()) {
596                     extraInfo.isDeleting = true;
597                     operationList_.try_emplace(i, extraInfo);
598                 } else {
599                     ThrowRepeatOperationError(i);
600                 }
601             }
602         } else {
603             ThrowRepeatOperationError(operation.index);
604         }
605     }
606 
OperateChange(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)607     void LazyForEachBuilder::OperateChange(V2::Operation& operation, int32_t& initialIndex,
608         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
609     {
610         OperationInfo itemInfo;
611         if (!ValidateIndex(operation.index, operation.type)) {
612             return;
613         }
614         auto indexExist = operationList_.find(operation.index);
615         if (indexExist == operationList_.end()) {
616             itemInfo.isChanged = true;
617             auto iter = cachedTemp.find(operation.index);
618             if (iter == cachedTemp.end()) {
619                 iter = expiringTemp.find(operation.index);
620             }
621             if (iter == expiringTemp.end()) {
622                 return;
623             }
624             if (!operation.key.empty()) {
625                 itemInfo.key = operation.key;
626             } else {
627                 itemInfo.key = iter->second.first;
628             }
629             initialIndex = std::min(initialIndex, operation.index);
630             operationList_.try_emplace(operation.index, itemInfo);
631         } else {
632             ThrowRepeatOperationError(operation.index);
633         }
634     }
635 
OperateMove(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)636     void LazyForEachBuilder::OperateMove(V2::Operation& operation, int32_t& initialIndex,
637         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
638     {
639         OperationInfo fromInfo;
640         OperationInfo toInfo;
641         if (!ValidateIndex(operation.coupleIndex.first, operation.type) ||
642             !ValidateIndex(operation.coupleIndex.second, operation.type)) {
643             return;
644         }
645         auto fromIndexExist = operationList_.find(operation.coupleIndex.first);
646         auto toIndexExist = operationList_.find(operation.coupleIndex.second);
647         if (fromIndexExist == operationList_.end()) {
648             fromInfo.changeCount = -1;
649             fromInfo.isDeleting = true;
650             initialIndex = std::min(initialIndex, operation.coupleIndex.first);
651             operationList_.try_emplace(operation.coupleIndex.first, fromInfo);
652         } else {
653             ThrowRepeatOperationError(operation.coupleIndex.first);
654         }
655         if (toIndexExist == operationList_.end()) {
656             toInfo.changeCount = 1;
657             auto iter = cachedTemp.find(operation.coupleIndex.first);
658             if (iter == cachedTemp.end()) {
659                 iter = expiringTemp.find(operation.coupleIndex.first);
660             }
661             if (iter == expiringTemp.end()) {
662                 return;
663             }
664             toInfo.node = iter->second.second;
665             toInfo.moveIn = true;
666             toInfo.fromDiffTo = operation.coupleIndex.first - operation.coupleIndex.second;
667             if (!operation.key.empty()) {
668                 toInfo.key = operation.key;
669             } else {
670                 toInfo.key = iter->second.first;
671             }
672             initialIndex = std::min(initialIndex, operation.coupleIndex.second);
673             operationList_.try_emplace(operation.coupleIndex.second, toInfo);
674         } else {
675             ThrowRepeatOperationError(operation.coupleIndex.second);
676         }
677     }
678 
OperateExchange(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)679     void LazyForEachBuilder::OperateExchange(V2::Operation& operation, int32_t& initialIndex,
680         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
681     {
682         OperationInfo startInfo;
683         OperationInfo endInfo;
684         if (!ValidateIndex(operation.coupleIndex.first, operation.type) ||
685             !ValidateIndex(operation.coupleIndex.second, operation.type)) {
686             return;
687         }
688         auto startIndexExist = operationList_.find(operation.coupleIndex.first);
689         auto endIndexExist = operationList_.find(operation.coupleIndex.second);
690         if (startIndexExist == operationList_.end()) {
691             auto iter = FindItem(operation.coupleIndex.first, cachedTemp, expiringTemp);
692             // if item can't be find in cachedItems_ nor expiringItem_, set UI node to null
693             if (iter == expiringTemp.end()) {
694                 startInfo.node = nullptr;
695             } else {
696                 startInfo.node = iter->second.second;
697                 if (!operation.coupleKey.first.empty()) {
698                     startInfo.key = operation.coupleKey.first;
699                 } else {
700                     startInfo.key = iter->second.first;
701                 }
702             }
703             startInfo.isExchange = true;
704             initialIndex = std::min(initialIndex, operation.coupleIndex.second);
705             operationList_.try_emplace(operation.coupleIndex.second, startInfo);
706         } else {
707             ThrowRepeatOperationError(operation.coupleIndex.first);
708         }
709         if (endIndexExist == operationList_.end()) {
710             auto iter = FindItem(operation.coupleIndex.second, cachedTemp, expiringTemp);
711             // if item can't be find in cachedItems_ nor expiringItem_, set UI node to null
712             if (iter == expiringTemp.end()) {
713                 endInfo.node = nullptr;
714             } else {
715                 endInfo.node = iter->second.second;
716                 if (!operation.coupleKey.second.empty()) {
717                     endInfo.key = operation.coupleKey.second;
718                 } else {
719                     endInfo.key = iter->second.first;
720                 }
721             }
722             endInfo.isExchange = true;
723             initialIndex = std::min(initialIndex, operation.coupleIndex.first);
724             operationList_.try_emplace(operation.coupleIndex.first, endInfo);
725         } else {
726             ThrowRepeatOperationError(operation.coupleIndex.second);
727         }
728     }
729 
FindItem(int32_t index,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)730     std::map<int32_t, LazyForEachChild>::iterator LazyForEachBuilder::FindItem(int32_t index,
731         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
732     {
733         auto iterOfCached = cachedTemp.find(index);
734         auto iterOfExpiring = expiringTemp.find(index);
735         // if UI node can't be find in cachedTemp, find it in expiringTemp
736         if (iterOfCached == cachedTemp.end() || iterOfCached->second.second == nullptr) {
737             return iterOfExpiring;
738         } else {
739             return iterOfCached;
740         }
741     }
742 
OperateReload(std::map<int32_t,LazyForEachChild> & expiringTemp)743     void LazyForEachBuilder::OperateReload(std::map<int32_t, LazyForEachChild>& expiringTemp)
744     {
745         for (auto& [index, node] : expiringTemp) {
746             expiringItem_.emplace(node.first, LazyForEachCacheChild(index, node.second));
747         }
748         operationList_.clear();
749         OnDataReloaded();
750     }
751 
ThrowRepeatOperationError(int32_t index)752     void LazyForEachBuilder::ThrowRepeatOperationError(int32_t index)
753     {
754         TAG_LOGE(AceLogTag::ACE_LAZY_FOREACH, "Repeat Operation for index: %{public}d", index);
755     }
756 
RecycleChildByIndex(int32_t index)757     void LazyForEachBuilder::RecycleChildByIndex(int32_t index)
758     {
759         auto iter = cachedItems_.find(index);
760         if (iter != cachedItems_.end()) {
761             if (!iter->second.second) {
762                 return;
763             }
764             auto dummyNode = AceType::DynamicCast<RecycleDummyNode>(iter->second.second);
765             if (!dummyNode) {
766                 return;
767             }
768             auto keyIter = expiringItem_.find(iter->second.first);
769             if (keyIter != expiringItem_.end() && keyIter->second.second) {
770                 expiringItem_.erase(keyIter);
771             }
772             cachedItems_.erase(index);
773         }
774     }
775 
PreBuild(int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)776     bool LazyForEachBuilder::PreBuild(int64_t deadline, const std::optional<LayoutConstraintF>& itemConstraint,
777         bool canRunLongPredictTask)
778     {
779         ACE_SYNTAX_SCOPED_TRACE("expiringItem_ count:[%zu]", expiringItem_.size());
780         outOfBoundaryNodes_.clear();
781         if (itemConstraint && !canRunLongPredictTask) {
782             return false;
783         }
784         auto count = OnGetTotalCount();
785         std::unordered_map<std::string, LazyForEachCacheChild> cache;
786         std::set<int32_t> idleIndexes;
787         // if List contains a mixture of ListItem, LazyForeach, and Foreach...
788         // Then both startIndex_ and endIndex_ can be -1
789         CheckCacheIndex(idleIndexes, count);
790 
791         ProcessCachedIndex(cache, idleIndexes);
792 
793         bool result = true;
794         result = ProcessPreBuildingIndex(cache, deadline, itemConstraint, canRunLongPredictTask, idleIndexes);
795         if (!result) {
796             expiringItem_.swap(cache);
797             return result;
798         }
799 
800         for (auto index : idleIndexes) {
801             result = PreBuildByIndex(index, cache, deadline, itemConstraint, canRunLongPredictTask);
802             if (!result) {
803                 break;
804             }
805         }
806         expiringItem_.swap(cache);
807         return result;
808     }
809 
RecordOutOfBoundaryNodes(int32_t index)810     void LazyForEachBuilder::RecordOutOfBoundaryNodes(int32_t index)
811     {
812         outOfBoundaryNodes_.emplace_back(index);
813     }
814 
RecycleItemsOutOfBoundary()815     void LazyForEachBuilder::RecycleItemsOutOfBoundary()
816     {
817         for (const auto& i: outOfBoundaryNodes_) {
818             RecycleChildByIndex(i);
819         }
820         outOfBoundaryNodes_.clear();
821     }
822 
UpdateMoveFromTo(int32_t from,int32_t to)823     void LazyForEachBuilder::UpdateMoveFromTo(int32_t from, int32_t to)
824     {
825         if (moveFromTo_) {
826             moveFromTo_.value().second = to;
827         } else {
828             moveFromTo_ = { from, to };
829         }
830     }
831 
ResetMoveFromTo()832     void LazyForEachBuilder::ResetMoveFromTo()
833     {
834         moveFromTo_.reset();
835     }
836 
ConvertFormToIndex(int32_t index)837     int32_t LazyForEachBuilder::ConvertFormToIndex(int32_t index)
838     {
839         if (!moveFromTo_) {
840             return index;
841         }
842         if (moveFromTo_.value().second == index) {
843             return moveFromTo_.value().first;
844         }
845         if (moveFromTo_.value().first <= index && index < moveFromTo_.value().second) {
846             return index + 1;
847         }
848         if (moveFromTo_.value().second < index && index <= moveFromTo_.value().first) {
849             return index - 1;
850         }
851         return index;
852     }
853 
RemoveAllChild()854     void LazyForEachBuilder::RemoveAllChild()
855     {
856         ACE_SYNTAX_SCOPED_TRACE("LazyForEach RemoveAllChild");
857         for (auto& [index, node] : cachedItems_) {
858             if (!node.second) {
859                 continue;
860             }
861             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
862             if (frameNode) {
863                 frameNode->SetActive(false);
864             }
865             auto tempNode = node.second;
866             auto pair = expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
867             if (!pair.second) {
868                 TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Use repeat key for index: %{public}d", index);
869                 ProcessOffscreenNode(tempNode, true);
870             }
871         }
872     }
873 
SetActiveChildRange(int32_t start,int32_t end)874     bool LazyForEachBuilder::SetActiveChildRange(int32_t start, int32_t end)
875     {
876         ACE_SYNTAX_SCOPED_TRACE("LazyForEach active range start[%d], end[%d]", start, end);
877         startIndex_ = start;
878         endIndex_ = end;
879         int32_t count = GetTotalCount();
880         UpdateHistoricalTotalCount(count);
881         bool needBuild = false;
882         for (auto& [index, node] : cachedItems_) {
883             bool isInRange = (index < count) && ((start <= end && start <= index && end >= index) ||
884                 (start > end && (index <= end || index >= start)));
885             if (!isInRange) {
886                 if (!node.second) {
887                     continue;
888                 }
889                 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
890                 if (frameNode) {
891                     frameNode->SetActive(false);
892                 }
893                 auto tempNode = node.second;
894                 auto pair = expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
895                 if (!pair.second) {
896                     TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Use repeat key for index: %{public}d", index);
897                     ProcessOffscreenNode(tempNode, true);
898                 }
899                 needBuild = true;
900                 continue;
901             }
902             if (node.second) {
903                     auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
904                     if (frameNode) {
905                         frameNode->SetActive(true);
906                     }
907                     continue;
908                 }
909                 auto keyIter = expiringItem_.find(node.first);
910                 if (keyIter != expiringItem_.end() && keyIter->second.second) {
911                     node.second = keyIter->second.second;
912                     expiringItem_.erase(keyIter);
913                     auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
914                     if (frameNode) {
915                         frameNode->SetActive(true);
916                     }
917                 }
918                 needBuild = true;
919         }
920         return needBuild;
921     }
922 
GetChildIndex(const RefPtr<FrameNode> & targetNode)923     int32_t LazyForEachBuilder::GetChildIndex(const RefPtr<FrameNode>& targetNode)
924     {
925         for (auto& [index, node] : cachedItems_) {
926             if (node.second) {
927                 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
928                 if (frameNode == targetNode) {
929                     return index;
930                 }
931             }
932         }
933         for (auto& [key, node] : expiringItem_) {
934             if (!node.second) {
935                 continue;
936             }
937             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
938             if (frameNode && frameNode == targetNode) {
939                 return node.first;
940             }
941         }
942         return -1;
943     }
944 
CacheItem(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,const std::optional<LayoutConstraintF> & itemConstraint,int64_t deadline,bool & isTimeout)945     RefPtr<UINode> LazyForEachBuilder::CacheItem(int32_t index,
946         std::unordered_map<std::string, LazyForEachCacheChild>& cache,
947         const std::optional<LayoutConstraintF>& itemConstraint, int64_t deadline, bool& isTimeout)
948     {
949         ACE_SCOPED_TRACE("Builder:BuildLazyItem [%d]", index);
950         auto itemInfo = OnGetChildByIndex(ConvertFormToIndex(index), expiringItem_);
951         CHECK_NULL_RETURN(itemInfo.second, nullptr);
952         auto pair = cache.try_emplace(itemInfo.first, LazyForEachCacheChild(index, itemInfo.second));
953         auto context = itemInfo.second->GetContext();
954         CHECK_NULL_RETURN(context, itemInfo.second);
955         auto frameNode = AceType::DynamicCast<FrameNode>(itemInfo.second->GetFrameChildByIndex(0, false, true));
956         context->SetPredictNode(frameNode);
957         if (!itemInfo.second->RenderCustomChild(deadline)) {
958             isTimeout = true;
959             context->ResetPredictNode();
960             return itemInfo.second;
961         }
962         if (pair.second) {
963             ProcessOffscreenNode(itemInfo.second, false);
964         } else {
965             TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Use repeat key for index: %{public}d", index);
966         }
967 
968         itemInfo.second->Build(nullptr);
969         context->ResetPredictNode();
970         itemInfo.second->SetJSViewActive(false, true);
971         cachedItems_[index] = LazyForEachChild(itemInfo.first, nullptr);
972 
973         return itemInfo.second;
974     }
975 
CheckCacheIndex(std::set<int32_t> & idleIndexes,int32_t count)976     void LazyForEachBuilder::CheckCacheIndex(std::set<int32_t>& idleIndexes, int32_t count)
977     {
978         if (count == 0) {
979             return;
980         }
981         for (int32_t i = 1; i <= cacheCount_ - endShowCached_; i++) {
982             if (isLoop_) {
983                 if ((startIndex_ <= endIndex_ && endIndex_ + i < count) ||
984                     startIndex_ > endIndex_ + i) {
985                     idleIndexes.emplace(endIndex_ + i);
986                 } else if ((endIndex_ + i) % count < startIndex_) {
987                     idleIndexes.emplace((endIndex_ + i) % count);
988                 }
989             } else {
990                 if (endIndex_ + i >= 0 && endIndex_ + i < count) {
991                     idleIndexes.emplace(endIndex_ + i);
992                 }
993             }
994         }
995         for (int32_t i = 1; i <= cacheCount_ - startShowCached_; i++) {
996             if (isLoop_) {
997                 if ((startIndex_ <= endIndex_ && startIndex_ >= i) ||
998                     startIndex_ > endIndex_ + i) {
999                     idleIndexes.emplace(startIndex_ - i);
1000                 } else if ((startIndex_ - i + count) % count > endIndex_) {
1001                     idleIndexes.emplace((startIndex_ - i + count) % count);
1002                 }
1003             } else {
1004                 if (startIndex_ - i >= 0 && startIndex_ - i < count) {
1005                     idleIndexes.emplace(startIndex_ - i);
1006                 }
1007             }
1008         }
1009     }
1010 
PreBuildByIndex(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)1011     bool LazyForEachBuilder::PreBuildByIndex(int32_t index,
1012         std::unordered_map<std::string, LazyForEachCacheChild>& cache, int64_t deadline,
1013         const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask)
1014     {
1015         if (GetSysTimestamp() > deadline) {
1016             if (DeleteExpiringItemImmediately()) {
1017                 return false;
1018             }
1019             for (const auto& [key, node] : expiringItem_) {
1020                 if (node.first == -1) {
1021                     cache.try_emplace(key, node);
1022                 }
1023             }
1024             return false;
1025         }
1026         bool isTimeout = false;
1027         preBuildingIndex_ = -1;
1028         auto uiNode = CacheItem(index, cache, itemConstraint, deadline, isTimeout);
1029         if (isTimeout) {
1030             preBuildingIndex_ = index;
1031             return false;
1032         }
1033         if (!canRunLongPredictTask && itemConstraint) {
1034             return false;
1035         }
1036         if (canRunLongPredictTask && uiNode && itemConstraint) {
1037             RefPtr<FrameNode> frameNode = DynamicCast<FrameNode>(uiNode);
1038             while (!frameNode) {
1039                 auto tempNode = uiNode;
1040                 uiNode = tempNode->GetFirstChild();
1041                 if (!uiNode) {
1042                     break;
1043                 }
1044                 frameNode = DynamicCast<FrameNode>(uiNode);
1045             }
1046             if (frameNode) {
1047                 frameNode->GetGeometryNode()->SetParentLayoutConstraint(itemConstraint.value());
1048                 FrameNode::ProcessOffscreenNode(frameNode);
1049             }
1050         }
1051         return true;
1052     }
1053 
ProcessCachedIndex(std::unordered_map<std::string,LazyForEachCacheChild> & cache,std::set<int32_t> & idleIndexes)1054     void LazyForEachBuilder::ProcessCachedIndex(std::unordered_map<std::string, LazyForEachCacheChild>& cache,
1055         std::set<int32_t>& idleIndexes)
1056     {
1057         auto expiringIter = expiringItem_.begin();
1058         while (expiringIter != expiringItem_.end()) {
1059             const auto& key = expiringIter->first;
1060             const auto& node = expiringIter->second;
1061             auto iter = idleIndexes.find(node.first);
1062             if (iter != idleIndexes.end() && node.second) {
1063                 LoadCacheByIndex(cache, idleIndexes, node, key, iter, expiringIter);
1064             } else {
1065                 LoadCacheByKey(cache, idleIndexes, node, key, expiringIter);
1066             }
1067         }
1068     }
1069 
ProcessOffscreenNode(RefPtr<UINode> uiNode,bool remove)1070     void LazyForEachBuilder::ProcessOffscreenNode(RefPtr<UINode> uiNode, bool remove)
1071     {
1072         if (uiNode) {
1073             auto frameNode = DynamicCast<FrameNode>(uiNode);
1074             while (!frameNode) {
1075                 auto tempNode = uiNode;
1076                 uiNode = tempNode->GetFirstChild();
1077                 if (!uiNode) {
1078                     break;
1079                 }
1080                 frameNode = DynamicCast<FrameNode>(uiNode);
1081             }
1082             if (frameNode) {
1083                 if (!remove) {
1084                     Inspector::AddOffscreenNode(frameNode);
1085                 } else {
1086                     Inspector::RemoveOffscreenNode(frameNode);
1087                 }
1088             }
1089         }
1090     }
1091 
GetAllChildren()1092     const std::map<int32_t, LazyForEachChild>& LazyForEachBuilder::GetAllChildren()
1093     {
1094         if (!cachedItems_.empty()) {
1095             startIndex_ = cachedItems_.begin()->first;
1096             endIndex_ = cachedItems_.rbegin()->first;
1097         }
1098         if (isLoop_ && !cachedItems_.empty()) {
1099             int32_t lastIndex = -1;
1100             for (auto& [index, node] : cachedItems_) {
1101                 if (lastIndex > -1 && index - lastIndex > 1) {
1102                     startIndex_ = index;
1103                     endIndex_ = lastIndex;
1104                     break;
1105                 }
1106             }
1107         }
1108         return cachedItems_;
1109     }
1110 
SetJSViewActive(bool active)1111     void LazyForEachBuilder::SetJSViewActive(bool active)
1112     {
1113         for (const auto& node : cachedItems_) {
1114             if (node.second.second == nullptr) {
1115                 continue;
1116             }
1117             node.second.second->SetJSViewActive(active, true);
1118         }
1119         for (const auto& node : expiringItem_) {
1120             if (node.second.second == nullptr) {
1121                 continue;
1122             }
1123             node.second.second->SetJSViewActive(active, true);
1124         }
1125     }
1126 
PaintDebugBoundaryTreeAll(bool flag)1127     void LazyForEachBuilder::PaintDebugBoundaryTreeAll(bool flag)
1128     {
1129         for (const auto& node : cachedItems_) {
1130             if (node.second.second == nullptr) {
1131                 continue;
1132             }
1133             node.second.second->PaintDebugBoundaryTreeAll(flag);
1134         }
1135         for (const auto& node : expiringItem_) {
1136             if (node.second.second == nullptr) {
1137                 continue;
1138             }
1139             node.second.second->PaintDebugBoundaryTreeAll(flag);
1140         }
1141     }
SetDestroying(bool isDestroying,bool cleanStatus)1142     void LazyForEachBuilder::SetDestroying(bool isDestroying, bool cleanStatus)
1143     {
1144         for (const auto& node : cachedItems_) {
1145             if (node.second.second == nullptr) {
1146                 continue;
1147             }
1148             if (node.second.second->IsReusableNode()) {
1149                 node.second.second->SetDestroying(isDestroying, false);
1150             } else {
1151                 node.second.second->SetDestroying(isDestroying, cleanStatus);
1152             }
1153         }
1154 
1155         for (const auto& node : expiringItem_) {
1156             if (node.second.second == nullptr) {
1157                 continue;
1158             }
1159             if (node.second.second->IsReusableNode()) {
1160                 node.second.second->SetDestroying(isDestroying, false);
1161             } else {
1162                 node.second.second->SetDestroying(isDestroying, cleanStatus);
1163             }
1164         }
1165     }
1166 }
1167