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_view_common_def.h"
17
18#include <string>
19#include <type_traits>
20
21#include "base/geometry/dimension.h"
22#include "base/log/log.h"
23#include "bridge/declarative_frontend/view_stack_processor.h"
24#include "bridge/declarative_frontend/jsview/js_view_abstract.h"
25#include "core/components/common/properties/color.h"
26
27namespace OHOS::Ace::Framework {
28
29template<class T>
30JSRef<JSVal> ConvertToJSValue(T&& value)
31{
32    using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
33    if constexpr (std::is_arithmetic_v<ValueType> || std::is_same_v<ValueType, std::string>) {
34        return JSRef<JSVal>::Make(ToJSValue(std::forward<T>(value)));
35    } else if constexpr (std::is_enum_v<ValueType>) {
36        return JSRef<JSVal>::Make(ToJSValue(static_cast<std::make_signed_t<ValueType>>(value)));
37    } else if constexpr (std::is_same_v<ValueType, Dimension> || std::is_same_v<ValueType, CalcDimension>) {
38        if (value.Unit() == DimensionUnit::VP) {
39            return JSRef<JSVal>::Make(ToJSValue(value.Value()));
40        } else {
41            LOGE("Failed to convert to JS value with dimension which it not using 'VP' unit");
42            return JSRef<JSVal>();
43        }
44    } else {
45        LOGE("Failed to convert to JS value");
46        return JSRef<JSVal>();
47    }
48}
49
50template<class T>
51void ConvertToJSValuesImpl(std::vector<JSRef<JSVal>>& result, T&& value)
52{
53    result.emplace_back(ConvertToJSValue(std::forward<T>(value)));
54}
55
56template<class T, class V, class... Args>
57void ConvertToJSValuesImpl(std::vector<JSRef<JSVal>>& result, T&& value, V&& nextValue, Args&&... args)
58{
59    result.emplace_back(ConvertToJSValue(std::forward<T>(value)));
60    ConvertToJSValuesImpl(result, std::forward<V>(nextValue), std::forward<Args>(args)...);
61}
62
63template<class... Args>
64std::vector<JSRef<JSVal>> ConvertToJSValues(Args... args)
65{
66    std::vector<JSRef<JSVal>> result;
67    ConvertToJSValuesImpl(result, args...);
68    return result;
69}
70
71template<class T>
72bool ConvertFromJSValueNG(const JSRef<JSVal>& jsValue, T& result)
73{
74    if constexpr (std::is_same_v<T, bool>) {
75        if (jsValue->IsBoolean()) {
76            result = jsValue->ToBoolean();
77            return true;
78        }
79        result = false;
80    } else if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) {
81        double value;
82        if (JSViewAbstract::ParseJsDouble(jsValue, value)) {
83            result = static_cast<T>(value);
84            return true;
85        }
86        result = 0;
87    } else if constexpr (std::is_same_v<T, std::string>) {
88        if (jsValue->IsString()) {
89            result = jsValue->ToString();
90            return true;
91        }
92    } else if constexpr (std::is_same_v<T, Dimension>) {
93        CalcDimension calc;
94        bool ret = JSViewAbstract::ParseJsDimensionVpNG(jsValue, calc);
95        result = calc;
96        return ret;
97    } else if constexpr (std::is_same_v<T, CalcDimension>) {
98        return JSViewAbstract::ParseJsDimensionVpNG(jsValue, result);
99    } else if constexpr (std::is_same_v<T, NG::CalcLength>) {
100        return JSViewAbstract::ParseJsLengthVpNG(jsValue, result);
101    } else if constexpr (std::is_same_v<T, Color>) {
102        return JSViewAbstract::ParseJsColor(jsValue, result);
103    }
104    return false;
105}
106
107template<class T>
108bool ConvertFromJSValue(const JSRef<JSVal>& jsValue, T& result)
109{
110    if constexpr (std::is_same_v<T, bool>) {
111        if (jsValue->IsBoolean()) {
112            result = jsValue->ToBoolean();
113            return true;
114        }
115        result = false;
116    } else if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) {
117        double value;
118        if (JSViewAbstract::ParseJsDouble(jsValue, value)) {
119            result = static_cast<T>(value);
120            return true;
121        }
122        result = 0;
123    } else if constexpr (std::is_same_v<T, std::string>) {
124        if (jsValue->IsString()) {
125            result = jsValue->ToString();
126            return true;
127        }
128    } else if constexpr (std::is_same_v<T, Dimension>) {
129        CalcDimension calc;
130        bool ret = JSViewAbstract::ParseJsDimensionVp(jsValue, calc);
131        result = calc;
132        return ret;
133    } else if constexpr (std::is_same_v<T, CalcDimension>) {
134        return JSViewAbstract::ParseJsDimensionVp(jsValue, result);
135    } else if constexpr (std::is_same_v<T, Color>) {
136        return JSViewAbstract::ParseJsColor(jsValue, result);
137    }
138    return false;
139}
140
141template<class T, size_t N>
142bool ConvertFromJSValue(const JSRef<JSVal>& jsValue, const T (&enumValues)[N], T& result)
143{
144    int32_t value = 0;
145    if (!ConvertFromJSValue(jsValue, value) || value < 0 || static_cast<size_t>(value) >= N) {
146        return false;
147    }
148    result = enumValues[value];
149    return true;
150}
151
152template<class T>
153T FastConvertFromJSValue(const JSRef<JSVal>& jsValue)
154{
155    T result;
156    if (!ConvertFromJSValue(jsValue, result)) {
157        LOGE("Failed to convert from JS value");
158    }
159    return result;
160}
161
162template<class C, class V, class T, size_t N>
163void JSViewSetProperty(void (C::*setMethod)(V), int32_t param, const T (&enumValues)[N], T defValue)
164{
165#ifndef NG_BUILD
166    auto component = AceType::DynamicCast<C>(ViewStackProcessor::GetInstance()->GetMainComponent());
167    if (!component) {
168        LOGW("Failed to get '%{public}s' in view stack", AceType::TypeName<C>());
169        return;
170    }
171    T value = defValue;
172    if (param >= 0 && static_cast<size_t>(param) < N) {
173        value = enumValues[param];
174    }
175    ((*component).*setMethod)(value);
176#else
177    LOGE("do not support JSViewSetProperty in new pipeline");
178#endif
179}
180
181template<class C, class V, class T>
182void JSViewSetProperty(void (C::*setMethod)(V), T&& param)
183{
184#ifndef NG_BUILD
185    auto component = AceType::DynamicCast<C>(ViewStackProcessor::GetInstance()->GetMainComponent());
186    if (!component) {
187        LOGW("Failed to get '%{public}s' in view stack", AceType::TypeName<C>());
188        return;
189    }
190    ((*component).*setMethod)(std::forward<T>(param));
191#else
192    LOGE("do not support JSViewSetProperty in new pipeline");
193#endif
194}
195
196template<class C, class F>
197bool JSViewBindEvent(
198    void (C::*setMethod)(std::function<F>&&), const JSExecutionContext& context, const JSRef<JSVal>& jsValue)
199{
200#ifndef NG_BUILD
201    if (!jsValue->IsFunction()) {
202        LOGW("Argument is not a function object");
203        return false;
204    }
205    auto component = AceType::DynamicCast<C>(ViewStackProcessor::GetInstance()->GetMainComponent());
206    if (!component) {
207        LOGW("Failed to get '%{public}s' in view stack", AceType::TypeName<C>());
208        return false;
209    }
210    ((*component).*setMethod)(JsEventCallback<F>(context, JSRef<JSFunc>::Cast(jsValue)));
211    return true;
212#else
213    LOGE("do not support JSViewBindEvent in new pipeline");
214    return false;
215#endif
216}
217
218template<class C, class F>
219bool JSViewBindEvent(void (C::*setMethod)(std::function<F>&&), const JSCallbackInfo& args)
220{
221    if (args.Length() < 1) {
222        LOGW("Must contain at least 1 argument");
223        return false;
224    }
225    return JSViewBindEvent(setMethod, args.GetExecutionContext(), args[0]);
226}
227
228} // namespace OHOS::Ace::Framework
229