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> & cachedItems)133 std::pair<std::string, RefPtr<NG::UINode>> OnGetChildByIndex( 134 int32_t index, std::unordered_map<std::string, NG::LazyForEachCacheChild>& cachedItems) 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 auto cachedIter = cachedItems.find(key); 147 if (cachedIter != cachedItems.end()) { 148 info.first = key; 149 info.second = cachedIter->second.second; 150 cachedItems.erase(cachedIter); 151 return info; 152 } 153 154 auto nodeIter = changedLazyForEachNodes_.find(index); 155 if (updateChangedNodeFlag_ && nodeIter != changedLazyForEachNodes_.end()) { 156 RefPtr<NG::UINode> lazyForEachNode = nodeIter->second; 157 info.first = key; 158 info.second = lazyForEachNode; 159 params[paramType::Initialize] = JSRef<JSVal>::Make(ToJSValue(false)); 160 params[paramType::ElmtIds] = SetToJSVal(dependElementIds_[lazyForEachNode].first); 161 162 auto jsElmtIds = itemGenFunc_->Call(JSRef<JSObject>(), paramType::MAX_PARAMS_SIZE, params); 163 std::string lastKey = UpdateDependElmtIds(info.second, jsElmtIds, key); 164 changedLazyForEachNodes_.erase(nodeIter); 165 cachedItems.erase(lastKey); 166 return info; 167 } 168 169 NG::ScopedViewStackProcessor scopedViewStackProcessor; 170 auto* viewStack = NG::ViewStackProcessor::GetInstance(); 171 if (parentView_) { 172 parentView_->MarkLazyForEachProcess(key); 173 } 174 viewStack->PushKey(key); 175 params[paramType::Initialize] = JSRef<JSVal>::Make(ToJSValue(true)); 176 JSRef<JSVal> jsElmtIds = itemGenFunc_->Call(JSRef<JSObject>(), paramType::MIN_PARAMS_SIZE, params); 177 viewStack->PopKey(); 178 if (parentView_) { 179 parentView_->ResetLazyForEachProcess(); 180 } 181 info.first = key; 182 info.second = viewStack->Finish(); 183 if (updateChangedNodeFlag_) { 184 UpdateDependElmtIds(info.second, jsElmtIds, key); 185 } 186 return info; 187 } 188 OnGetChildByIndexNew(int32_t index,std::map<int32_t,NG::LazyForEachChild> & cachedItems,std::unordered_map<std::string,NG::LazyForEachCacheChild> & expiringItems)189 std::pair<std::string, RefPtr<NG::UINode>> OnGetChildByIndexNew(int32_t index, 190 std::map<int32_t, NG::LazyForEachChild>& cachedItems, 191 std::unordered_map<std::string, NG::LazyForEachCacheChild>& expiringItems) override 192 { 193 std::pair<std::string, RefPtr<NG::UINode>> info; 194 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, info); 195 if (getDataFunc_.IsEmpty()) { 196 return info; 197 } 198 199 JSRef<JSVal> params[paramType::MAX_PARAMS_SIZE]; 200 params[paramType::Data] = CallJSFunction(getDataFunc_, dataSourceObj_, index); 201 params[paramType::Index] = JSRef<JSVal>::Make(ToJSValue(index)); 202 std::string key; 203 auto cachedIter = cachedItems.find(index); 204 if (cachedIter != cachedItems.end() && !cachedIter->second.first.empty()) { 205 key = cachedIter->second.first; 206 } else { 207 key = keyGenFunc_(params[paramType::Data], index); 208 } 209 210 auto expiringIter = expiringItems.find(key); 211 if (expiringIter != expiringItems.end()) { 212 info.first = key; 213 info.second = expiringIter->second.second; 214 expiringItems.erase(expiringIter); 215 // if info.second is null, the following ui node creation process is needed to fill info.second 216 if (info.second != nullptr) { 217 return info; 218 } 219 } 220 221 NG::ScopedViewStackProcessor scopedViewStackProcessor; 222 auto* viewStack = NG::ViewStackProcessor::GetInstance(); 223 if (parentView_) { 224 parentView_->MarkLazyForEachProcess(key); 225 } 226 viewStack->PushKey(key); 227 params[paramType::Initialize] = JSRef<JSVal>::Make(ToJSValue(true)); 228 itemGenFunc_->Call(JSRef<JSObject>(), paramType::MIN_PARAMS_SIZE, params); 229 viewStack->PopKey(); 230 if (parentView_) { 231 parentView_->ResetLazyForEachProcess(); 232 } 233 info.first = key; 234 info.second = viewStack->Finish(); 235 return info; 236 } 237 ReleaseChildGroupById(const std::string & id)238 void ReleaseChildGroupById(const std::string& id) override 239 { 240 JSLazyForEachActuator::ReleaseChildGroupByComposedId(id); 241 } 242 RegisterDataChangeListener(const RefPtr<V2::DataChangeListener> & listener)243 void RegisterDataChangeListener(const RefPtr<V2::DataChangeListener>& listener) override 244 { 245 JSLazyForEachActuator::RegisterListener(listener); 246 } 247 UnregisterDataChangeListener(V2::DataChangeListener * listener)248 void UnregisterDataChangeListener(V2::DataChangeListener* listener) override 249 { 250 JSLazyForEachActuator::UnregisterListener(listener); 251 } 252 253 ACE_DISALLOW_COPY_AND_MOVE(JSLazyForEachBuilder); 254 255 private: 256 std::map<int32_t, RefPtr<NG::UINode>> changedLazyForEachNodes_; 257 std::map<RefPtr<NG::UINode>, std::pair<std::set<uint32_t>, std::string>> dependElementIds_; 258 enum paramType {Data = 0, Index, Initialize, ElmtIds, MIN_PARAMS_SIZE = ElmtIds, MAX_PARAMS_SIZE}; 259 }; 260 261 } // namespace OHOS::Ace::Framework 262 263 #endif // FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_LAZY_FOREACH_BUILDER_H 264