1 /*
2 * Copyright (c) 2021 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 "bridge/declarative_frontend/jsview/js_foreach.h"
17
18 #include <string>
19
20 #include "base/memory/referenced.h"
21 #include "core/components_ng/syntax/for_each_model_ng.h"
22 #include "bridge/declarative_frontend/jsview/models/for_each_model_impl.h"
23 #include "bridge/declarative_frontend/engine/functions/js_foreach_function.h"
24 #include "bridge/declarative_frontend/view_stack_processor.h"
25 #include "core/common/container.h"
26
27
28 namespace OHOS::Ace {
29
30 std::unique_ptr<ForEachModel> ForEachModel::instance = nullptr;
31 std::mutex ForEachModel::mutex_;
32
GetInstance()33 ForEachModel* ForEachModel::GetInstance()
34 {
35 if (!instance) {
36 std::lock_guard<std::mutex> lock(mutex_);
37 if (!instance) {
38 #ifdef NG_BUILD
39 instance.reset(new NG::ForEachModelNG());
40 #else
41 if (Container::IsCurrentUseNewPipeline()) {
42 instance.reset(new NG::ForEachModelNG());
43 } else {
44 instance.reset(new Framework::ForEachModelImpl());
45 }
46 #endif
47 }
48 }
49 return instance.get();
50 }
51 } // namespace OHOS::Ace
52
53
54 namespace OHOS::Ace::Framework {
55
56 // Create(...)
57 // NG: no params
58 // Classic: cmpilerGenId, array, itemGenFunc, idGenFunction
Create(const JSCallbackInfo & info)59 void JSForEach::Create(const JSCallbackInfo& info)
60 {
61
62 if (Container::IsCurrentUseNewPipeline()) {
63 ForEachModel::GetInstance()->Create();
64 return;
65 }
66
67 if (info.Length() < 4 || !info[2]->IsObject() || !info[3]->IsFunction() ||
68 (!info[0]->IsNumber() && !info[0]->IsString()) || info[1]->IsUndefined() || !info[1]->IsObject()) {
69 TAG_LOGW(AceLogTag::ACE_FOREACH, "Invalid arguments for ForEach");
70 return;
71 }
72
73 JSRef<JSObject> jsArray = JSRef<JSObject>::Cast(info[2]);
74 JSRef<JSVal> jsViewMapperFunc = info[3];
75 JSRef<JSVal> jsIdentityMapperFunc;
76 RefPtr<JsForEachFunction> jsForEachFunction;
77 if (info.Length() > 4 && info[4]->IsFunction()) {
78 jsIdentityMapperFunc = info[4];
79 jsForEachFunction = AceType::MakeRefPtr<JsForEachFunction>(
80 jsArray, JSRef<JSFunc>::Cast(jsIdentityMapperFunc), JSRef<JSFunc>::Cast(jsViewMapperFunc));
81 } else {
82 jsForEachFunction = AceType::MakeRefPtr<JsForEachFunction>(jsArray, JSRef<JSFunc>::Cast(jsViewMapperFunc));
83 }
84
85 OHOS::Ace::ForEachFunc forEachFunc = { [jsForEachFunction]() { return jsForEachFunction->ExecuteIdentityMapper(); },
86 [jsForEachFunction](int32_t index) { jsForEachFunction->ExecuteBuilderForIndex(index); } };
87 ForEachModel::GetInstance()->Create(info[0]->ToString(), forEachFunc);
88 }
89
Pop()90 void JSForEach::Pop()
91 {
92 ForEachModel::GetInstance()->Pop();
93 }
94
95 // partial update / NG only
96 // signature
97 // nodeId/elmtId : number
98 // idList : string[]
99 // returns bool, true on success
GetIdArray(const JSCallbackInfo & info)100 void JSForEach::GetIdArray(const JSCallbackInfo& info)
101 {
102 if ((info.Length() != 2) || !info[1]->IsArray() || info[0]->IsString()) {
103 TAG_LOGW(AceLogTag::ACE_FOREACH, "Invalid arguments for ForEach.GetIdArray");
104 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
105 return;
106 }
107
108 JSRef<JSArray> jsArr = JSRef<JSArray>::Cast(info[1]);
109 if (jsArr->Length() > 0) {
110 TAG_LOGW(AceLogTag::ACE_FOREACH, "JS Array must be empty!");
111 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
112 return;
113 }
114
115 const auto elmtId = info[0]->ToNumber<int32_t>();
116 std::list<std::string> idList = ForEachModel::GetInstance()->GetCurrentIdList(elmtId);
117
118 size_t index = 0;
119 for (const auto& id : idList) {
120 jsArr->SetValueAt(index++, JSRef<JSVal>::Make(ToJSValue(id.c_str())));
121 }
122 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(index > 0)));
123 }
124
125 // Partial update / NG only
126 // Gets idList as a input and stores it.
127 // Fill diffIds with new indexes as an output.
128 // Fill duplicateIds with duplica IDs detected.
129 // nodeId/elmtId : number
130 // idList : string[]
131 // diffIds : number[]
132 // duplicateIds : number[]
133 // no return value
SetIdArray(const JSCallbackInfo & info)134 void JSForEach::SetIdArray(const JSCallbackInfo& info)
135 {
136 if (info.Length() != 4 ||
137 !info[0]->IsNumber() || !info[1]->IsArray() ||
138 !info[2]->IsArray() || !info[3]->IsArray()) {
139 TAG_LOGW(AceLogTag::ACE_FOREACH, "Invalid arguments for ForEach.SetIdArray");
140 return;
141 }
142
143 const auto elmtId = info[0]->ToNumber<int32_t>();
144 JSRef<JSArray> jsArr = JSRef<JSArray>::Cast(info[1]);
145 JSRef<JSArray> diffIds = JSRef<JSArray>::Cast(info[2]);
146 JSRef<JSArray> duplicateIds = JSRef<JSArray>::Cast(info[3]);
147 std::list<std::string> newIdArr;
148
149 if (diffIds->Length() > 0 || duplicateIds->Length() > 0) {
150 TAG_LOGW(AceLogTag::ACE_FOREACH, "Invalid arguments for ForEach.SetIdArray output arrays must be empty!");
151 return;
152 }
153
154 const std::list<std::string>& previousIDList = ForEachModel::GetInstance()->GetCurrentIdList(elmtId);
155 std::unordered_set<std::string> oldIdsSet(previousIDList.begin(), previousIDList.end());
156 std::unordered_set<std::string> newIds;
157
158 size_t diffIndx = 0;
159 size_t duplicateIndx = 0;
160 for (size_t i = 0; i < jsArr->Length(); i++) {
161 JSRef<JSVal> strId = jsArr->GetValueAt(i);
162 // Save return value of insert to know was it duplicate...
163 std::pair<std::unordered_set<std::string>::iterator, bool> ret = newIds.insert(strId->ToString());
164
165 // Duplicate Id detected. Will return index of those to caller.
166 if (!ret.second) {
167 duplicateIds->SetValueAt(duplicateIndx++, JSRef<JSVal>::Make(ToJSValue(i)));
168 } else {
169 // ID was not duplicate. Accept it.
170 newIdArr.emplace_back(*ret.first);
171 // Check was ID previously available or totally new one.
172 if (oldIdsSet.find(*ret.first) == oldIdsSet.end()) {
173 // Populate output diff array with this index that was not in old array.
174 diffIds->SetValueAt(diffIndx++, JSRef<JSVal>::Make(ToJSValue(i)));
175 }
176 }
177 }
178
179 ForEachModel::GetInstance()->SetNewIds(std::move(newIdArr));
180 }
181
182 // signature is
183 // id: string | number
184 // parentView : JSView
CreateNewChildStart(const JSCallbackInfo & info)185 void JSForEach::CreateNewChildStart(const JSCallbackInfo& info)
186 {
187 if ((info.Length() != 2) || !info[1]->IsObject() || (!info[0]->IsNumber() && !info[0]->IsString())) {
188 return;
189 }
190
191 const auto id = info[0]->ToString();
192 ForEachModel::GetInstance()->CreateNewChildStart(id);
193 }
194
195 // signature is
196 // id: string | number
197 // parentView : JSView
CreateNewChildFinish(const JSCallbackInfo & info)198 void JSForEach::CreateNewChildFinish(const JSCallbackInfo& info)
199 {
200 if ((info.Length() != 2) || !info[1]->IsObject() || (!info[0]->IsNumber() && !info[0]->IsString())) {
201 return;
202 }
203
204 const auto id = info[0]->ToString();
205 ForEachModel::GetInstance()->CreateNewChildFinish(id);
206 }
207
JSBind(BindingTarget globalObj)208 void JSForEach::JSBind(BindingTarget globalObj)
209 {
210 JSClass<JSForEach>::Declare("ForEach");
211 JSClass<JSForEach>::StaticMethod("create", &JSForEach::Create);
212 JSClass<JSForEach>::StaticMethod("pop", &JSForEach::Pop);
213 JSClass<JSForEach>::StaticMethod("getIdArray", &JSForEach::GetIdArray);
214 JSClass<JSForEach>::StaticMethod("setIdArray", &JSForEach::SetIdArray);
215 JSClass<JSForEach>::StaticMethod("createNewChildStart", &JSForEach::CreateNewChildStart);
216 JSClass<JSForEach>::StaticMethod("createNewChildFinish", &JSForEach::CreateNewChildFinish);
217 JSClass<JSForEach>::Bind<>(globalObj);
218 }
219
220 } // namespace OHOS::Ace::Framework
221