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         if (from < to) {
250             for (const auto& [itemIndex, child] : temp) {
251                 auto position = static_cast<size_t>(itemIndex);
252                 if (position > from && position <= to && position >= 1) {
253                     cachedItems_.emplace(position - 1, child);
254                 } else if (position == from) {
255                     cachedItems_.emplace(to, child);
256                 } else {
257                     cachedItems_.emplace(itemIndex, child);
258                 }
259             }
260         } else {
261             for (const auto& [itemIndex, child] : temp) {
262                 auto position = static_cast<size_t>(itemIndex);
263                 if (position >= to && position < from) {
264                     cachedItems_.emplace(position + 1, child);
265                 } else if (position == from) {
266                     cachedItems_.emplace(to, child);
267                 } else {
268                     cachedItems_.emplace(itemIndex, child);
269                 }
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_.try_emplace(
285                 fromIter->second.first, LazyForEachCacheChild(to, std::move(fromIter->second.second)));
286             cachedItems_.erase(fromIter);
287         } else if (toIter != cachedItems_.end()) {
288             expiringItem_.try_emplace(
289                 toIter->second.first, LazyForEachCacheChild(from, std::move(toIter->second.second)));
290             cachedItems_.erase(toIter);
291         }
292         return true;
293     }
294 
GetAllItems(std::vector<UINode * > & items)295     void LazyForEachBuilder::GetAllItems(std::vector<UINode*>& items)
296     {
297         for (const auto& item : cachedItems_) {
298             items.emplace_back(RawPtr(item.second.second));
299         }
300         for (const auto& item : expiringItem_) {
301             items.emplace_back(RawPtr(item.second.second));
302         }
303         for (const auto& item : nodeList_) {
304             items.emplace_back(RawPtr(item.second));
305         }
306     }
307 
GetTotalCountOfOriginalDataset()308     int32_t LazyForEachBuilder::GetTotalCountOfOriginalDataset()
309     {
310         int32_t totalCount = GetTotalCount();
311         int32_t totalCountOfOriginalDataset = historicalTotalCount_;
312         UpdateHistoricalTotalCount(totalCount);
313         return totalCountOfOriginalDataset;
314     }
315 
OnDatasetChange(std::list<V2::Operation> DataOperations)316     std::pair<int32_t, std::list<std::pair<std::string, RefPtr<UINode>>>> LazyForEachBuilder::OnDatasetChange(
317         std::list<V2::Operation> DataOperations)
318     {
319         totalCountOfOriginalDataset_ = GetTotalCountOfOriginalDataset();
320         int32_t initialIndex = totalCountOfOriginalDataset_;
321         std::map<int32_t, LazyForEachChild> expiringTempItem_;
322         std::list<std::string> expiringKeys;
323         for (auto& [key, cacheChild] : expiringItem_) {
324             if (cacheChild.first > -1) {
325                 expiringTempItem_.try_emplace(cacheChild.first, LazyForEachChild(key, cacheChild.second));
326                 expiringKeys.emplace_back(key);
327             }
328         }
329         for (auto& key : expiringKeys) {
330             expiringItem_.erase(key);
331         }
332         decltype(expiringTempItem_) expiringTemp(std::move(expiringTempItem_));
333         for (auto operation : DataOperations) {
334             bool isReload = ClassifyOperation(operation, initialIndex, cachedItems_, expiringTemp);
335             if (isReload) {
336                 initialIndex = 0;
337                 return std::pair(initialIndex, nodeList_);
338             }
339         }
340         decltype(cachedItems_) cachedTemp(std::move(cachedItems_));
341         std::map<int32_t, int32_t> indexChangedMap;
342         CollectIndexChangedCount(indexChangedMap);
343         RepairDatasetItems(cachedTemp, cachedItems_, indexChangedMap);
344         RepairDatasetItems(expiringTemp, expiringTempItem_, indexChangedMap);
345         for (auto& [index, node] : expiringTempItem_) {
346             expiringItem_.emplace(node.first, LazyForEachCacheChild(index, node.second));
347         }
348         operationList_.clear();
349         return std::pair(initialIndex, nodeList_);
350     }
351 
RepairDatasetItems(std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTempItem_,std::map<int32_t,int32_t> & indexChangedMap)352     void LazyForEachBuilder::RepairDatasetItems(std::map<int32_t, LazyForEachChild>& cachedTemp,
353         std::map<int32_t, LazyForEachChild>& expiringTempItem_, std::map<int32_t, int32_t>& indexChangedMap)
354     {
355         int32_t changedIndex = 0;
356         for (auto& [index, child] : cachedTemp) {
357             auto iter = indexChangedMap.find(index);
358             if (iter == indexChangedMap.end()) {
359                 if (!indexChangedMap.empty()) {
360                     iter--;
361                     if (iter->first < index) {
362                         changedIndex = iter->second;
363                     }
364                 }
365             } else {
366                 changedIndex = iter->second;
367             }
368             if (operationList_.find(index) == operationList_.end()) {
369                 expiringTempItem_.try_emplace(index + changedIndex, child);
370                 continue;
371             }
372             if (!indexChangedMap.empty()) {
373                 changedIndex = iter->second;
374             }
375             auto info = operationList_.find(index)->second;
376             if (info.isDeleting) {
377                 nodeList_.emplace_back(child.first, child.second);
378             } else if (info.isChanged) {
379                 expiringTempItem_.try_emplace(index + changedIndex, LazyForEachChild(info.key, nullptr));
380             } else if (!info.extraKey.empty()) {
381                 expiringTempItem_.try_emplace(index + changedIndex, child);
382                 int32_t preChangedIndex = 0;
383                 auto preIter = indexChangedMap.find(index - 1);
384                 if (preIter != indexChangedMap.end()) {
385                     preChangedIndex = preIter->second;
386                 }
387                 for (int32_t i = 0; i < static_cast<int32_t>(info.extraKey.size()); i++) {
388                     expiringTempItem_.try_emplace(
389                         index + preChangedIndex + i, LazyForEachChild(info.extraKey[i], nullptr));
390                 }
391             } else {
392                 RepairMoveOrExchange(expiringTempItem_, info, child, index, changedIndex);
393             }
394         }
395     }
396 
RepairMoveOrExchange(std::map<int32_t,LazyForEachChild> & expiringTempItem_,OperationInfo & info,LazyForEachChild & child,int32_t index,int32_t changedIndex)397     void LazyForEachBuilder::RepairMoveOrExchange(std::map<int32_t, LazyForEachChild>& expiringTempItem_,
398         OperationInfo& info, LazyForEachChild& child, int32_t index, int32_t changedIndex)
399     {
400         if (info.isExchange) {
401             // if the child will be exchanged with a null node, this child should be deleted
402             if (info.node == nullptr && child.second != nullptr) {
403                 nodeList_.emplace_back(child.first, child.second);
404             }
405             // null node should not be put in expiringTempItem_ then expiringTempItem_
406             if (info.node != nullptr) {
407                 expiringTempItem_.try_emplace(index + changedIndex, LazyForEachChild(info.key, info.node));
408             }
409             return;
410         }
411         if (info.moveIn) {
412             int32_t fromIndex = index + changedIndex - 1;
413             int32_t toIndex = index + changedIndex;
414             if (info.fromDiffTo > 0) {
415                 fromIndex = index + changedIndex;
416                 toIndex = index + changedIndex - 1;
417             }
418             expiringTempItem_.try_emplace(toIndex, LazyForEachChild(info.key, info.node));
419             expiringTempItem_.try_emplace(fromIndex, child);
420         }
421     }
422 
CollectIndexChangedCount(std::map<int32_t,int32_t> & indexChangedMap)423     void LazyForEachBuilder::CollectIndexChangedCount(std::map<int32_t, int32_t>& indexChangedMap)
424     {
425         int32_t changedIndex = 0;
426         for (auto& [index, operationInfo] : operationList_) {
427             if (indexChangedMap.size() >= static_cast<size_t>(1)) {
428                 for (int32_t i = indexChangedMap.rbegin()->first + 1; i < index; i++) {
429                     indexChangedMap.try_emplace(i, changedIndex);
430                 }
431             }
432             operationInfo.changeCount += changedIndex;
433             changedIndex = operationInfo.changeCount;
434             indexChangedMap.try_emplace(index, changedIndex);
435         }
436     }
437 
ClassifyOperation(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)438     bool LazyForEachBuilder::ClassifyOperation(V2::Operation& operation, int32_t& initialIndex,
439         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
440     {
441         switch (operationTypeMap[operation.type]) {
442             case OP::ADD:
443                 OperateAdd(operation, initialIndex);
444                 break;
445             case OP::DEL:
446                 OperateDelete(operation, initialIndex);
447                 break;
448             case OP::CHANGE:
449                 OperateChange(operation, initialIndex, cachedTemp, expiringTemp);
450                 break;
451             case OP::MOVE:
452                 OperateMove(operation, initialIndex, cachedTemp, expiringTemp);
453                 break;
454             case OP::EXCHANGE:
455                 OperateExchange(operation, initialIndex, cachedTemp, expiringTemp);
456                 break;
457             case OP::RELOAD:
458                 OperateReload(expiringTemp);
459                 return true;
460         }
461         return false;
462     }
463 
ValidateIndex(int32_t index,const std::string & type)464     bool LazyForEachBuilder::ValidateIndex(int32_t index, const std::string& type)
465     {
466         bool isValid = true;
467         if (operationTypeMap[type] == OP::ADD) {
468             // for add operation, the index can equal totalCountOfOriginalDataset_
469             isValid = index >= 0 && index <= totalCountOfOriginalDataset_;
470         } else {
471             isValid = index >= 0 && index < totalCountOfOriginalDataset_;
472         }
473         if (!isValid) {
474             TAG_LOGE(
475                 AceLogTag::ACE_LAZY_FOREACH, "%{public}s(%{public}d) Operation is out of range", type.c_str(), index);
476         }
477         return isValid;
478     }
479 
OperateAdd(V2::Operation & operation,int32_t & initialIndex)480     void LazyForEachBuilder::OperateAdd(V2::Operation& operation, int32_t& initialIndex)
481     {
482         OperationInfo itemInfo;
483         if (!ValidateIndex(operation.index, operation.type)) {
484             return;
485         }
486         auto indexExist = operationList_.find(operation.index);
487         if (indexExist == operationList_.end()) {
488             itemInfo.changeCount = operation.count;
489             if (!operation.key.empty()) {
490                 itemInfo.extraKey.push_back(operation.key);
491             } else if (operation.keyList.size() >= static_cast<size_t>(1)) {
492                 for (std::string key : operation.keyList) {
493                     itemInfo.extraKey.push_back(key);
494                 }
495             }
496             initialIndex = std::min(initialIndex, operation.index);
497             operationList_.try_emplace(operation.index, itemInfo);
498         } else {
499             ThrowRepeatOperationError(operation.index);
500         }
501     }
502 
OperateDelete(V2::Operation & operation,int32_t & initialIndex)503     void LazyForEachBuilder::OperateDelete(V2::Operation& operation, int32_t& initialIndex)
504     {
505         OperationInfo itemInfo;
506         if (!ValidateIndex(operation.index, operation.type)) {
507             return;
508         }
509         auto indexExist = operationList_.find(operation.index);
510         if (indexExist == operationList_.end()) {
511             itemInfo.changeCount = -operation.count;
512             itemInfo.isDeleting = true;
513             initialIndex = std::min(initialIndex, operation.index);
514             operationList_.try_emplace(operation.index, itemInfo);
515             for (int32_t i = operation.index + 1; i < operation.index + operation.count; i++) {
516                 OperationInfo extraInfo;
517                 if (operationList_.find(i) == operationList_.end()) {
518                     extraInfo.isDeleting = true;
519                     operationList_.try_emplace(i, extraInfo);
520                 } else {
521                     ThrowRepeatOperationError(i);
522                 }
523             }
524         } else {
525             ThrowRepeatOperationError(operation.index);
526         }
527     }
528 
OperateChange(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)529     void LazyForEachBuilder::OperateChange(V2::Operation& operation, int32_t& initialIndex,
530         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
531     {
532         OperationInfo itemInfo;
533         if (!ValidateIndex(operation.index, operation.type)) {
534             return;
535         }
536         auto indexExist = operationList_.find(operation.index);
537         if (indexExist == operationList_.end()) {
538             itemInfo.isChanged = true;
539             auto iter = cachedTemp.find(operation.index);
540             if (iter == cachedTemp.end()) {
541                 iter = expiringTemp.find(operation.index);
542             }
543             if (iter == expiringTemp.end()) {
544                 return;
545             }
546             if (!operation.key.empty()) {
547                 itemInfo.key = operation.key;
548             } else {
549                 itemInfo.key = iter->second.first;
550             }
551             initialIndex = std::min(initialIndex, operation.index);
552             operationList_.try_emplace(operation.index, itemInfo);
553         } else {
554             ThrowRepeatOperationError(operation.index);
555         }
556     }
557 
OperateMove(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)558     void LazyForEachBuilder::OperateMove(V2::Operation& operation, int32_t& initialIndex,
559         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
560     {
561         OperationInfo fromInfo;
562         OperationInfo toInfo;
563         if (!ValidateIndex(operation.coupleIndex.first, operation.type) ||
564             !ValidateIndex(operation.coupleIndex.second, operation.type)) {
565             return;
566         }
567         auto fromIndexExist = operationList_.find(operation.coupleIndex.first);
568         auto toIndexExist = operationList_.find(operation.coupleIndex.second);
569         if (fromIndexExist == operationList_.end()) {
570             fromInfo.changeCount = -1;
571             fromInfo.isDeleting = true;
572             initialIndex = std::min(initialIndex, operation.coupleIndex.first);
573             operationList_.try_emplace(operation.coupleIndex.first, fromInfo);
574         } else {
575             ThrowRepeatOperationError(operation.coupleIndex.first);
576         }
577         if (toIndexExist == operationList_.end()) {
578             toInfo.changeCount = 1;
579             auto iter = cachedTemp.find(operation.coupleIndex.first);
580             if (iter == cachedTemp.end()) {
581                 iter = expiringTemp.find(operation.coupleIndex.first);
582             }
583             if (iter == expiringTemp.end()) {
584                 return;
585             }
586             toInfo.node = iter->second.second;
587             toInfo.moveIn = true;
588             toInfo.fromDiffTo = operation.coupleIndex.first - operation.coupleIndex.second;
589             if (!operation.key.empty()) {
590                 toInfo.key = operation.key;
591             } else {
592                 toInfo.key = iter->second.first;
593             }
594             initialIndex = std::min(initialIndex, operation.coupleIndex.second);
595             operationList_.try_emplace(operation.coupleIndex.second, toInfo);
596         } else {
597             ThrowRepeatOperationError(operation.coupleIndex.second);
598         }
599     }
600 
OperateExchange(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)601     void LazyForEachBuilder::OperateExchange(V2::Operation& operation, int32_t& initialIndex,
602         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
603     {
604         OperationInfo startInfo;
605         OperationInfo endInfo;
606         if (!ValidateIndex(operation.coupleIndex.first, operation.type) ||
607             !ValidateIndex(operation.coupleIndex.second, operation.type)) {
608             return;
609         }
610         auto startIndexExist = operationList_.find(operation.coupleIndex.first);
611         auto endIndexExist = operationList_.find(operation.coupleIndex.second);
612         if (startIndexExist == operationList_.end()) {
613             auto iter = FindItem(operation.coupleIndex.first, cachedTemp, expiringTemp);
614             // if item can't be find in cachedItems_ nor expiringItem_, set UI node to null
615             if (iter == expiringTemp.end()) {
616                 startInfo.node = nullptr;
617             } else {
618                 startInfo.node = iter->second.second;
619                 if (!operation.coupleKey.first.empty()) {
620                     startInfo.key = operation.coupleKey.first;
621                 } else {
622                     startInfo.key = iter->second.first;
623                 }
624             }
625             startInfo.isExchange = true;
626             initialIndex = std::min(initialIndex, operation.coupleIndex.second);
627             operationList_.try_emplace(operation.coupleIndex.second, startInfo);
628         } else {
629             ThrowRepeatOperationError(operation.coupleIndex.first);
630         }
631         if (endIndexExist == operationList_.end()) {
632             auto iter = FindItem(operation.coupleIndex.second, cachedTemp, expiringTemp);
633             // if item can't be find in cachedItems_ nor expiringItem_, set UI node to null
634             if (iter == expiringTemp.end()) {
635                 endInfo.node = nullptr;
636             } else {
637                 endInfo.node = iter->second.second;
638                 if (!operation.coupleKey.second.empty()) {
639                     endInfo.key = operation.coupleKey.second;
640                 } else {
641                     endInfo.key = iter->second.first;
642                 }
643             }
644             endInfo.isExchange = true;
645             initialIndex = std::min(initialIndex, operation.coupleIndex.first);
646             operationList_.try_emplace(operation.coupleIndex.first, endInfo);
647         } else {
648             ThrowRepeatOperationError(operation.coupleIndex.second);
649         }
650     }
651 
FindItem(int32_t index,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)652     std::map<int32_t, LazyForEachChild>::iterator LazyForEachBuilder::FindItem(int32_t index,
653         std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp)
654     {
655         auto iterOfCached = cachedTemp.find(index);
656         auto iterOfExpiring = expiringTemp.find(index);
657         // if UI node can't be find in cachedTemp, find it in expiringTemp
658         if (iterOfCached == cachedTemp.end() || iterOfCached->second.second == nullptr) {
659             return iterOfExpiring;
660         } else {
661             return iterOfCached;
662         }
663     }
664 
OperateReload(std::map<int32_t,LazyForEachChild> & expiringTemp)665     void LazyForEachBuilder::OperateReload(std::map<int32_t, LazyForEachChild>& expiringTemp)
666     {
667         for (auto& [index, node] : expiringTemp) {
668             expiringItem_.emplace(node.first, LazyForEachCacheChild(index, node.second));
669         }
670         operationList_.clear();
671         OnDataReloaded();
672     }
673 
ThrowRepeatOperationError(int32_t index)674     void LazyForEachBuilder::ThrowRepeatOperationError(int32_t index)
675     {
676         TAG_LOGE(AceLogTag::ACE_LAZY_FOREACH, "Repeat Operation for index: %{public}d", index);
677     }
678 
RecycleChildByIndex(int32_t index)679     void LazyForEachBuilder::RecycleChildByIndex(int32_t index)
680     {
681         auto iter = cachedItems_.find(index);
682         if (iter != cachedItems_.end()) {
683             if (!iter->second.second) {
684                 return;
685             }
686             auto dummyNode = AceType::DynamicCast<RecycleDummyNode>(iter->second.second);
687             if (!dummyNode) {
688                 return;
689             }
690             auto keyIter = expiringItem_.find(iter->second.first);
691             if (keyIter != expiringItem_.end() && keyIter->second.second) {
692                 expiringItem_.erase(keyIter);
693             }
694             cachedItems_.erase(index);
695         }
696     }
697 
PreBuild(int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)698     bool LazyForEachBuilder::PreBuild(int64_t deadline, const std::optional<LayoutConstraintF>& itemConstraint,
699         bool canRunLongPredictTask)
700     {
701         ACE_SYNTAX_SCOPED_TRACE("expiringItem_ count:[%zu]", expiringItem_.size());
702         outOfBoundaryNodes_.clear();
703         if (itemConstraint && !canRunLongPredictTask) {
704             return false;
705         }
706         auto count = OnGetTotalCount();
707         std::unordered_map<std::string, LazyForEachCacheChild> cache;
708         std::set<int32_t> idleIndexes;
709         if (startIndex_ != -1 && endIndex_ != -1) {
710             CheckCacheIndex(idleIndexes, count);
711         }
712 
713         ProcessCachedIndex(cache, idleIndexes);
714 
715         bool result = true;
716         result = ProcessPreBuildingIndex(cache, deadline, itemConstraint, canRunLongPredictTask, idleIndexes);
717         if (!result) {
718             expiringItem_.swap(cache);
719             return result;
720         }
721 
722         for (auto index : idleIndexes) {
723             result = PreBuildByIndex(index, cache, deadline, itemConstraint, canRunLongPredictTask);
724             if (!result) {
725                 break;
726             }
727         }
728         expiringItem_.swap(cache);
729         return result;
730     }
731 
RecordOutOfBoundaryNodes(int32_t index)732     void LazyForEachBuilder::RecordOutOfBoundaryNodes(int32_t index)
733     {
734         outOfBoundaryNodes_.emplace_back(index);
735     }
736 
RecycleItemsOutOfBoundary()737     void LazyForEachBuilder::RecycleItemsOutOfBoundary()
738     {
739         for (const auto& i: outOfBoundaryNodes_) {
740             RecycleChildByIndex(i);
741         }
742         outOfBoundaryNodes_.clear();
743     }
744 
UpdateMoveFromTo(int32_t from,int32_t to)745     void LazyForEachBuilder::UpdateMoveFromTo(int32_t from, int32_t to)
746     {
747         if (moveFromTo_) {
748             moveFromTo_.value().second = to;
749         } else {
750             moveFromTo_ = { from, to };
751         }
752     }
753 
ResetMoveFromTo()754     void LazyForEachBuilder::ResetMoveFromTo()
755     {
756         moveFromTo_.reset();
757     }
758 
ConvertFormToIndex(int32_t index)759     int32_t LazyForEachBuilder::ConvertFormToIndex(int32_t index)
760     {
761         if (!moveFromTo_) {
762             return index;
763         }
764         if (moveFromTo_.value().second == index) {
765             return moveFromTo_.value().first;
766         }
767         if (moveFromTo_.value().first <= index && index < moveFromTo_.value().second) {
768             return index + 1;
769         }
770         if (moveFromTo_.value().second < index && index <= moveFromTo_.value().first) {
771             return index - 1;
772         }
773         return index;
774     }
775 }
776