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