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