• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 #ifndef FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_LAZY_FOREACH_BUILDER_H
17 #define FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_LAZY_FOREACH_BUILDER_H
18 
19 #include <functional>
20 #include <set>
21 #include <string>
22 
23 #include "base/memory/ace_type.h"
24 #include "bridge/declarative_frontend/jsview/js_lazy_foreach_actuator.h"
25 #include "bridge/declarative_frontend/jsview/js_view.h"
26 #include "core/components_ng/base/view_stack_processor.h"
27 #include "core/components_ng/syntax/lazy_for_each_builder.h"
28 
29 namespace OHOS::Ace::Framework {
30 
31 class JSLazyForEachBuilder : public NG::LazyForEachBuilder, public JSLazyForEachActuator {
32     DECLARE_ACE_TYPE(JSLazyForEachBuilder, NG::LazyForEachBuilder, JSLazyForEachActuator);
33 
34 public:
35     JSLazyForEachBuilder() = default;
~JSLazyForEachBuilder()36     ~JSLazyForEachBuilder()
37     {
38         changedLazyForEachNodes_.clear();
39         dependElementIds_.clear();
40     };
41 
OnGetTotalCount()42     int32_t OnGetTotalCount() override
43     {
44         return GetTotalIndexCount();
45     }
46 
OnExpandChildrenOnInitialInNG()47     void OnExpandChildrenOnInitialInNG() override
48     {
49         auto totalIndex = GetTotalIndexCount();
50         auto* stack = NG::ViewStackProcessor::GetInstance();
51         JSRef<JSVal> params[paramType::MIN_PARAMS_SIZE];
52         for (auto index = 0; index < totalIndex; index++) {
53             params[paramType::Data] = CallJSFunction(getDataFunc_, dataSourceObj_, index);
54             params[paramType::Index] = JSRef<JSVal>::Make(ToJSValue(index));
55             params[paramType::Initialize] = JSRef<JSVal>::Make(ToJSValue(true));
56             std::string key = keyGenFunc_(params[paramType::Data], index);
57             stack->PushKey(key);
58             itemGenFunc_->Call(JSRef<JSObject>(), paramType::MIN_PARAMS_SIZE, params);
59             stack->PopKey();
60         }
61     }
62 
NotifyDataAdded(size_t index)63     void NotifyDataAdded(size_t index) override
64     {
65         if (updateChangedNodeFlag_) {
66             decltype(changedLazyForEachNodes_) temp(std::move(changedLazyForEachNodes_));
67             for (auto& [oldindex, child] : temp) {
68                 changedLazyForEachNodes_.try_emplace(
69                     index > static_cast<size_t>(oldindex) ? oldindex : oldindex + 1, std::move(child));
70             }
71         }
72     }
73 
NotifyDataChanged(size_t index,const RefPtr<NG::UINode> & lazyForEachNode,bool isRebuild)74     void NotifyDataChanged(size_t index, const RefPtr<NG::UINode>& lazyForEachNode, bool isRebuild) override
75     {
76         if (updateChangedNodeFlag_ && !isRebuild) {
77             changedLazyForEachNodes_[index] = lazyForEachNode;
78         }
79     }
80 
NotifyDataDeleted(const RefPtr<NG::UINode> & lazyForEachNode,size_t index,bool removeIds)81     void NotifyDataDeleted(const RefPtr<NG::UINode>& lazyForEachNode, size_t index, bool removeIds) override
82     {
83         if (updateChangedNodeFlag_) {
84             decltype(changedLazyForEachNodes_) temp(std::move(changedLazyForEachNodes_));
85             for (auto& [oldindex, child] : temp) {
86                 if (static_cast<size_t>(oldindex) != index && lazyForEachNode != child) {
87                     changedLazyForEachNodes_.try_emplace(
88                         index > static_cast<size_t>(oldindex) ? oldindex : oldindex - 1, std::move(child));
89                 }
90             }
91             if (removeIds) {
92                 dependElementIds_.erase(lazyForEachNode);
93             }
94         }
95     }
96 
KeepRemovedItemInCache(NG::LazyForEachChild node,std::unordered_map<std::string,NG::LazyForEachCacheChild> & cachedItems)97     void KeepRemovedItemInCache(NG::LazyForEachChild node,
98         std::unordered_map<std::string, NG::LazyForEachCacheChild>& cachedItems) override
99     {
100         if (updateChangedNodeFlag_) {
101             cachedItems.try_emplace(node.first, NG::LazyForEachCacheChild(-1, node.second));
102         }
103     }
104 
UpdateDependElmtIds(RefPtr<NG::UINode> & node,JSRef<JSVal> & jsElmtIds,std::string key)105     std::string UpdateDependElmtIds(RefPtr<NG::UINode>& node, JSRef<JSVal>& jsElmtIds, std::string key)
106     {
107         std::string lastKey;
108         if (jsElmtIds->IsArray()) {
109             JSRef<JSArray> jsElmtIdArray = JSRef<JSArray>::Cast(jsElmtIds);
110             for (size_t i = 0; i < jsElmtIdArray->Length(); i++) {
111                 JSRef<JSVal> jsElmtId = jsElmtIdArray->GetValueAt(i);
112                 if (jsElmtId->IsNumber()) {
113                     dependElementIds_[node].first.insert(jsElmtId->ToNumber<uint32_t>());
114                 }
115             }
116             lastKey = dependElementIds_[node].second;
117             dependElementIds_[node].second = key;
118         }
119         return lastKey;
120     }
121 
SetToJSVal(std::set<uint32_t> elmtIds)122     JSRef<JSVal> SetToJSVal(std::set<uint32_t> elmtIds)
123     {
124         JSRef<JSArray> jsElmtIdArray = JSRef<JSArray>::New();
125         int32_t count = 0;
126         for (auto& elmtId : elmtIds) {
127             jsElmtIdArray->SetValueAt(count, JSRef<JSVal>::Make(ToJSValue(elmtId)));
128             count++;
129         }
130         return JSRef<JSVal>::Cast(jsElmtIdArray);
131     }
132 
OnGetChildByIndex(int32_t index,std::unordered_map<std::string,NG::LazyForEachCacheChild> & expiringItems)133     std::pair<std::string, RefPtr<NG::UINode>> OnGetChildByIndex(
134         int32_t index, std::unordered_map<std::string, NG::LazyForEachCacheChild>& expiringItems) override
135     {
136         std::pair<std::string, RefPtr<NG::UINode>> info;
137         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, info);
138         if (getDataFunc_.IsEmpty()) {
139             return info;
140         }
141 
142         JSRef<JSVal> params[paramType::MAX_PARAMS_SIZE];
143         params[paramType::Data] = CallJSFunction(getDataFunc_, dataSourceObj_, index);
144         params[paramType::Index] = JSRef<JSVal>::Make(ToJSValue(index));
145         std::string key = keyGenFunc_(params[paramType::Data], index);
146 
147         GetChildFromExpiringItems(key, expiringItems, info);
148         // if info.second is null, the following ui node creation process is needed to fill info.second
149         if (info.second != nullptr) {
150             return info;
151         }
152 
153         auto nodeIter = changedLazyForEachNodes_.find(index);
154         if (updateChangedNodeFlag_ && nodeIter != changedLazyForEachNodes_.end()) {
155             RefPtr<NG::UINode> lazyForEachNode = nodeIter->second;
156             info.first = key;
157             info.second = lazyForEachNode;
158             params[paramType::Initialize] = JSRef<JSVal>::Make(ToJSValue(false));
159             params[paramType::ElmtIds] = SetToJSVal(dependElementIds_[lazyForEachNode].first);
160 
161             auto jsElmtIds = itemGenFunc_->Call(JSRef<JSObject>(), paramType::MAX_PARAMS_SIZE, params);
162             std::string lastKey = UpdateDependElmtIds(info.second, jsElmtIds, key);
163             changedLazyForEachNodes_.erase(nodeIter);
164             expiringItems.erase(lastKey);
165             return info;
166         }
167 
168         NG::ScopedViewStackProcessor scopedViewStackProcessor;
169         auto* viewStack = NG::ViewStackProcessor::GetInstance();
170         if (parentView_) {
171             parentView_->MarkLazyForEachProcess(key);
172         }
173         viewStack->PushKey(key);
174         params[paramType::Initialize] = JSRef<JSVal>::Make(ToJSValue(true));
175         JSRef<JSVal> jsElmtIds = itemGenFunc_->Call(JSRef<JSObject>(), paramType::MIN_PARAMS_SIZE, params);
176         viewStack->PopKey();
177         if (parentView_) {
178             parentView_->ResetLazyForEachProcess();
179         }
180         info.first = key;
181         info.second = viewStack->Finish();
182         if (updateChangedNodeFlag_) {
183             UpdateDependElmtIds(info.second, jsElmtIds, key);
184         }
185         return info;
186     }
187 
OnGetChildByIndexNew(int32_t index,std::map<int32_t,NG::LazyForEachChild> & cachedItems,std::unordered_map<std::string,NG::LazyForEachCacheChild> & expiringItems)188     std::pair<std::string, RefPtr<NG::UINode>> OnGetChildByIndexNew(int32_t index,
189         std::map<int32_t, NG::LazyForEachChild>& cachedItems,
190         std::unordered_map<std::string, NG::LazyForEachCacheChild>& expiringItems) override
191     {
192         std::pair<std::string, RefPtr<NG::UINode>> info;
193         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, info);
194         if (getDataFunc_.IsEmpty()) {
195             return info;
196         }
197 
198         JSRef<JSVal> params[paramType::MAX_PARAMS_SIZE];
199         params[paramType::Data] = CallJSFunction(getDataFunc_, dataSourceObj_, index);
200         params[paramType::Index] = JSRef<JSVal>::Make(ToJSValue(index));
201         std::string key;
202         auto cachedIter = cachedItems.find(index);
203         if (cachedIter != cachedItems.end() && !cachedIter->second.first.empty()) {
204             key = cachedIter->second.first;
205         } else {
206             key = keyGenFunc_(params[paramType::Data], index);
207         }
208 
209         GetChildFromExpiringItems(key, expiringItems, info);
210         // if info.second is null, the following ui node creation process is needed to fill info.second
211         if (info.second != nullptr) {
212             return info;
213         }
214 
215         NG::ScopedViewStackProcessor scopedViewStackProcessor;
216         auto* viewStack = NG::ViewStackProcessor::GetInstance();
217         if (parentView_) {
218             parentView_->MarkLazyForEachProcess(key);
219         }
220         viewStack->PushKey(key);
221         params[paramType::Initialize] = JSRef<JSVal>::Make(ToJSValue(true));
222         itemGenFunc_->Call(JSRef<JSObject>(), paramType::MIN_PARAMS_SIZE, params);
223         viewStack->PopKey();
224         if (parentView_) {
225             parentView_->ResetLazyForEachProcess();
226         }
227         info.first = key;
228         info.second = viewStack->Finish();
229         return info;
230     }
231 
ReleaseChildGroupById(const std::string & id)232     void ReleaseChildGroupById(const std::string& id) override
233     {
234         JSLazyForEachActuator::ReleaseChildGroupByComposedId(id);
235     }
236 
RegisterDataChangeListener(const RefPtr<V2::DataChangeListener> & listener)237     void RegisterDataChangeListener(const RefPtr<V2::DataChangeListener>& listener) override
238     {
239         JSLazyForEachActuator::RegisterListener(listener);
240     }
241 
UnregisterDataChangeListener(V2::DataChangeListener * listener)242     void UnregisterDataChangeListener(V2::DataChangeListener* listener) override
243     {
244         JSLazyForEachActuator::UnregisterListener(listener);
245     }
246 
247     ACE_DISALLOW_COPY_AND_MOVE(JSLazyForEachBuilder);
248 
249 private:
250     std::map<int32_t, RefPtr<NG::UINode>> changedLazyForEachNodes_;
251     std::map<RefPtr<NG::UINode>, std::pair<std::set<uint32_t>, std::string>> dependElementIds_;
252     enum paramType {Data = 0, Index, Initialize, ElmtIds, MIN_PARAMS_SIZE = ElmtIds, MAX_PARAMS_SIZE};
GetChildFromExpiringItems(std::string key,std::unordered_map<std::string,NG::LazyForEachCacheChild> & expiringItems,std::pair<std::string,RefPtr<NG::UINode>> & info)253     void GetChildFromExpiringItems(std::string key,
254         std::unordered_map<std::string, NG::LazyForEachCacheChild>& expiringItems,
255         std::pair<std::string, RefPtr<NG::UINode>>& info)
256     {
257         auto expiringIter = expiringItems.find(key);
258         if (expiringIter != expiringItems.end()) {
259             info.first = key;
260             info.second = expiringIter->second.second;
261             expiringIter->second.second = nullptr;
262         }
263     }
264 };
265 
266 } // namespace OHOS::Ace::Framework
267 
268 #endif // FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_LAZY_FOREACH_BUILDER_H
269