• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #include "core/components_ng/pattern/text_field/content_controller.h"
16 
17 #include <algorithm>
18 #include <cstdint>
19 #include <string>
20 
21 #include "base/utils/string_utils.h"
22 #include "base/utils/utils.h"
23 #include "core/components_ng/pattern/text/typed_text.h"
24 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
25 
26 namespace OHOS::Ace::NG {
27 namespace {
28 const std::string DIGIT_WHITE_LIST = "[0-9]";
29 const std::string DIGIT_DECIMAL_WHITE_LIST = "[0-9.]";
30 const std::string PHONE_WHITE_LIST = R"([\d\-\+\*\#]+)";
31 const std::string EMAIL_WHITE_LIST = "[\\w.\\@]";
32 const std::string URL_WHITE_LIST = "[a-zA-z]+://[^\\s]*";
33 // when do ai analaysis, we should list the left and right of the string
34 constexpr static int32_t AI_TEXT_RANGE_LEFT = 50;
35 constexpr static int32_t AI_TEXT_RANGE_RIGHT = 50;
36 } // namespace
37 
PreprocessString(int32_t startIndex,int32_t endIndex,const std::string & value)38 std::string ContentController::PreprocessString(int32_t startIndex, int32_t endIndex, const std::string& value)
39 {
40     auto tmp = value;
41     auto pattern = pattern_.Upgrade();
42     CHECK_NULL_RETURN(pattern, value);
43     auto textField = DynamicCast<TextFieldPattern>(pattern);
44     CHECK_NULL_RETURN(textField, value);
45     auto property = textField->GetLayoutProperty<TextFieldLayoutProperty>();
46     auto selectValue = GetSelectedValue(startIndex, endIndex);
47     if (property->GetTextInputType().has_value() &&
48         (property->GetTextInputType().value() == TextInputType::NUMBER_DECIMAL ||
49             property->GetTextInputType().value() == TextInputType::EMAIL_ADDRESS)) {
50         char specialChar = property->GetTextInputType().value() == TextInputType::NUMBER_DECIMAL ? '.' : '@';
51         if (content_.find(specialChar) != std::string::npos && value.find(specialChar) != std::string::npos &&
52             GetSelectedValue(startIndex, endIndex).find(specialChar) == std::string::npos) {
53             tmp.erase(
54                 std::remove_if(tmp.begin(), tmp.end(), [&specialChar](char c) { return c == specialChar; }), tmp.end());
55         }
56     }
57     FilterValueType(tmp);
58     auto wideText = GetWideText();
59     auto wideTmp = StringUtils::ToWstring(tmp);
60     auto maxLength = static_cast<uint32_t>(textField->GetMaxLength());
61     auto curLength = static_cast<uint32_t>(wideText.length());
62     auto addLength = static_cast<uint32_t>(wideTmp.length());
63     auto delLength = static_cast<uint32_t>(std::abs(endIndex - startIndex));
64     addLength = std::min(addLength, maxLength - curLength + delLength);
65     tmp = StringUtils::ToString(wideTmp.substr(0, addLength));
66     return tmp;
67 }
68 
InsertValue(int32_t index,const std::string & value)69 bool ContentController::InsertValue(int32_t index, const std::string& value)
70 {
71     return ReplaceSelectedValue(index, index, value);
72 }
73 
ReplaceSelectedValue(int32_t startIndex,int32_t endIndex,const std::string & value)74 bool ContentController::ReplaceSelectedValue(int32_t startIndex, int32_t endIndex, const std::string& value)
75 {
76     FormatIndex(startIndex, endIndex);
77     auto tmp = PreprocessString(startIndex, endIndex, value);
78     auto wideText = GetWideText();
79     auto str = content_;
80     content_ = StringUtils::ToString(wideText.substr(0, startIndex)) + tmp +
81                StringUtils::ToString(wideText.substr(endIndex, static_cast<int32_t>(wideText.length()) - endIndex));
82     auto len = content_.length();
83     FilterValue();
84     if (value.length() == 1 && content_.length() < len) {
85         content_ = str;
86     }
87     return !tmp.empty();
88 }
89 
GetSelectedValue(int32_t startIndex,int32_t endIndex)90 std::string ContentController::GetSelectedValue(int32_t startIndex, int32_t endIndex)
91 {
92     FormatIndex(startIndex, endIndex);
93     auto wideText = GetWideText();
94     return StringUtils::ToString(wideText.substr(startIndex, endIndex - startIndex));
95 }
96 
FormatIndex(int32_t & startIndex,int32_t & endIndex)97 void ContentController::FormatIndex(int32_t& startIndex, int32_t& endIndex)
98 {
99     startIndex = std::min(startIndex, endIndex);
100     endIndex = std::max(startIndex, endIndex);
101     auto wideText = GetWideText();
102     startIndex = std::clamp(startIndex, 0, static_cast<int32_t>(wideText.length()));
103     endIndex = std::clamp(endIndex, 0, static_cast<int32_t>(wideText.length()));
104 }
105 
FilterTextInputStyle(bool & textChanged,std::string & result)106 void ContentController::FilterTextInputStyle(bool& textChanged, std::string& result)
107 {
108     auto pattern = pattern_.Upgrade();
109     CHECK_NULL_VOID(pattern);
110     auto textField = DynamicCast<TextFieldPattern>(pattern);
111     CHECK_NULL_VOID(textField);
112     auto property = textField->GetLayoutProperty<TextFieldLayoutProperty>();
113     if (!property->GetTextInputType().has_value()) {
114         return;
115     }
116     switch (property->GetTextInputType().value()) {
117         case TextInputType::NUMBER: {
118             textChanged |= FilterWithEvent(DIGIT_WHITE_LIST, result);
119             break;
120         }
121         case TextInputType::PHONE: {
122             textChanged |= FilterWithEvent(PHONE_WHITE_LIST, result);
123             break;
124         }
125         case TextInputType::EMAIL_ADDRESS: {
126             textChanged |= FilterWithEvent(EMAIL_WHITE_LIST, result);
127             textChanged |= FilterWithEmail(result);
128             break;
129         }
130         case TextInputType::URL: {
131             textChanged |= FilterWithEvent(URL_WHITE_LIST, result);
132             break;
133         }
134         case TextInputType::VISIBLE_PASSWORD:
135         case TextInputType::NEW_PASSWORD: {
136             textChanged |= FilterWithAscii(result);
137             break;
138         }
139         case TextInputType::NUMBER_PASSWORD: {
140             textChanged |= FilterWithEvent(DIGIT_WHITE_LIST, result);
141             break;
142         }
143         case TextInputType::SCREEN_LOCK_PASSWORD: {
144             textChanged |= FilterWithAscii(result);
145             break;
146         }
147         case TextInputType::NUMBER_DECIMAL: {
148             textChanged |= FilterWithEvent(DIGIT_DECIMAL_WHITE_LIST, result);
149             textChanged |= FilterWithDecimal(result);
150             break;
151         }
152         default: {
153             break;
154         }
155     }
156 }
157 
FilterValue()158 void ContentController::FilterValue()
159 {
160     bool textChanged = false;
161     auto result = content_;
162     auto pattern = pattern_.Upgrade();
163     CHECK_NULL_VOID(pattern);
164     auto textField = DynamicCast<TextFieldPattern>(pattern);
165     CHECK_NULL_VOID(textField);
166     auto property = textField->GetLayoutProperty<TextFieldLayoutProperty>();
167 
168     bool hasInputFilter =
169         property->GetInputFilter().has_value() && !property->GetInputFilter().value().empty() && !content_.empty();
170     if (!hasInputFilter) {
171         FilterTextInputStyle(textChanged, result);
172     } else {
173         textChanged |= FilterWithEvent(property->GetInputFilter().value(), result);
174         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
175             FilterTextInputStyle(textChanged, result);
176         }
177     }
178     if (textChanged) {
179         content_ = result;
180     }
181     auto maxLength =
182         property->HasMaxLength() ? property->GetMaxLengthValue(Infinity<uint32_t>()) : Infinity<uint32_t>();
183     auto textWidth = static_cast<int32_t>(GetWideText().length());
184     if (GreatNotEqual(textWidth, maxLength)) {
185         content_ = StringUtils::ToString(GetWideText().substr(0, maxLength));
186     }
187 }
188 
FilterValueType(std::string & value)189 void ContentController::FilterValueType(std::string& value)
190 {
191     bool textChanged = false;
192     auto result = value;
193     auto pattern = pattern_.Upgrade();
194     CHECK_NULL_VOID(pattern);
195     auto textField = DynamicCast<TextFieldPattern>(pattern);
196     CHECK_NULL_VOID(textField);
197     auto property = textField->GetLayoutProperty<TextFieldLayoutProperty>();
198 
199     bool hasInputFilter = property->GetInputFilter().has_value() && !property->GetInputFilter().value().empty();
200     if (!hasInputFilter) {
201         FilterTextInputStyle(textChanged, result);
202     } else {
203         textChanged = FilterWithEvent(property->GetInputFilter().value(), result) || textChanged;
204         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
205             FilterTextInputStyle(textChanged, result);
206         }
207     }
208     if (textChanged) {
209         value = result;
210     }
211 }
212 
RemoveErrorTextFromValue(const std::string & value,const std::string & errorText)213 std::string ContentController::RemoveErrorTextFromValue(const std::string& value, const std::string& errorText)
214 {
215     std::string result;
216     int32_t valuePtr = 0;
217     int32_t errorTextPtr = 0;
218     auto valueSize = static_cast<int32_t>(value.length());
219     auto errorTextSize = static_cast<int32_t>(errorText.length());
220     while (errorTextPtr < errorTextSize) {
221         while (value[valuePtr] != errorText[errorTextPtr] && valuePtr < valueSize) {
222             result += value[valuePtr];
223             valuePtr++;
224         }
225         // no more text left to remove in value
226         if (valuePtr >= valueSize) {
227             return result;
228         }
229         // increase both value ptr and error text ptr if char in value is removed
230         valuePtr++;
231         errorTextPtr++;
232     }
233     result += value.substr(valuePtr);
234     return result;
235 }
236 
FilterWithRegex(const std::string & filter,std::string & result)237 std::string ContentController::FilterWithRegex(const std::string& filter, std::string& result)
238 {
239     std::regex filterRegex(filter);
240     auto errorText = std::regex_replace(result, filterRegex, "");
241     result = RemoveErrorTextFromValue(result, errorText);
242     return errorText;
243 }
244 
FilterWithEmail(std::string & result)245 bool ContentController::FilterWithEmail(std::string& result)
246 {
247     auto valueToUpdate = result;
248     bool first = true;
249     std::replace_if(
250         result.begin(), result.end(),
251         [&first](const char c) {
252             if (c == '@' && !first) {
253                 return true;
254             }
255             if (c == '@') {
256                 first = false;
257             }
258             return false;
259         },
260         ' ');
261 
262     // remove the spaces
263     result.erase(std::remove(result.begin(), result.end(), ' '), result.end());
264     return result != valueToUpdate;
265 }
266 
FilterWithAscii(std::string & result)267 bool ContentController::FilterWithAscii(std::string& result)
268 {
269     if (result.empty()) {
270         return false;
271     }
272     auto valueToUpdate = result;
273     bool textChange = true;
274     std::string errorText;
275     result.clear();
276     for (char valuePtr : valueToUpdate) {
277         if (isascii(valuePtr)) {
278             result += valuePtr;
279         } else {
280             errorText += valuePtr;
281         }
282     }
283     if (errorText.empty()) {
284         textChange = false;
285     } else {
286         LOGI("FilterWithAscii Error text %{private}s", errorText.c_str());
287     }
288     return textChange;
289 }
290 
FilterWithDecimal(std::string & result)291 bool ContentController::FilterWithDecimal(std::string& result)
292 {
293     auto valueToUpdate = result;
294     bool first = true;
295     std::replace_if(
296         result.begin(), result.end(),
297         [&first](const char c) {
298             if (c == '.' && !first) {
299                 return true;
300             }
301             if (c == '.') {
302                 first = false;
303             }
304             return false;
305         },
306         ' ');
307     result.erase(std::remove(result.begin(), result.end(), ' '), result.end());
308     return result != valueToUpdate;
309 }
310 
FilterWithEvent(const std::string & filter,std::string & result)311 bool ContentController::FilterWithEvent(const std::string& filter, std::string& result)
312 {
313     auto errorValue = FilterWithRegex(filter, result);
314     if (!errorValue.empty()) {
315         auto pattern = pattern_.Upgrade();
316         CHECK_NULL_RETURN(pattern, false);
317         auto textField = DynamicCast<TextFieldPattern>(pattern);
318         CHECK_NULL_RETURN(textField, false);
319         auto host = textField->GetHost();
320         CHECK_NULL_RETURN(host, false);
321         auto eventHub = host->GetEventHub<TextFieldEventHub>();
322         eventHub->FireOnInputFilterError(errorValue);
323         auto textFieldAccessibilityProperty = host->GetAccessibilityProperty<TextFieldAccessibilityProperty>();
324         CHECK_NULL_RETURN(textFieldAccessibilityProperty, false);
325         textFieldAccessibilityProperty->SetErrorText(errorValue);
326     }
327     return !errorValue.empty();
328 }
329 
erase(int32_t startIndex,int32_t length)330 void ContentController::erase(int32_t startIndex, int32_t length)
331 {
332     if (EraseEmoji()) {
333         return;
334     }
335     auto wideText = GetWideText().erase(startIndex, length);
336     content_ = StringUtils::ToString(wideText);
337 }
338 
GetValueBeforeIndex(int32_t index)339 std::string ContentController::GetValueBeforeIndex(int32_t index)
340 {
341     return StringUtils::ToString(GetWideText().substr(0, index));
342 }
343 
GetValueAfterIndex(int32_t index)344 std::string ContentController::GetValueAfterIndex(int32_t index)
345 {
346     return StringUtils::ToString(GetWideText().substr(index, GetWideText().length() - index));
347 }
348 
GetSelectedLimitValue(int32_t & index,int32_t & startIndex)349 std::string ContentController::GetSelectedLimitValue(int32_t& index, int32_t& startIndex)
350 {
351     startIndex = index - AI_TEXT_RANGE_LEFT;
352     int32_t endIndex = index + AI_TEXT_RANGE_RIGHT;
353     FormatIndex(startIndex, endIndex);
354     index = index - startIndex;
355     return GetSelectedValue(startIndex, endIndex);
356 }
357 
EraseEmoji()358 bool ContentController::EraseEmoji()
359 {
360     bool emojiFlag = false;
361     uint32_t startIndex = 0;
362     uint32_t endIndex = 0;
363     while (startIndex < content_.length()) {
364         auto unicode = TypedText::GetUTF8Next(content_.c_str(), startIndex, endIndex);
365         if (TypedText::IsEmoji(unicode)) {
366             content_.erase(startIndex, endIndex - startIndex);
367             emojiFlag = true;
368             continue;
369         }
370         startIndex = endIndex;
371     }
372     return emojiFlag;
373 }
374 } // namespace OHOS::Ace::NG