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
16 #include "core/components_ng/property/templates_parser.h"
17
18 #include <regex>
19
20 #include "base/utils/string_utils.h"
21 #include "base/utils/utils.h"
22 #include "core/pipeline/pipeline_base.h"
23
24 namespace OHOS::Ace::NG {
25 namespace {
26 constexpr double FULL_PERCENT = 100.0;
27 constexpr uint32_t REPEAT_MIN_SIZE = 6;
28 constexpr uint32_t CROSS_WIDTH = 2;
29 constexpr uint32_t STRETCH_MATCH_SIZE = 4;
30 const std::string UNIT_VP = "vp";
31 const std::string UNIT_PIXEL = "px";
32 const std::string UNIT_RATIO = "fr";
33 const std::string UNIT_PERCENT = "%";
34 const std::string REPEAT_PREFIX = "repeat";
35 const std::string UNIT_AUTO_FILL = "auto-fill";
36 const std::string UNIT_AUTO_FIT = "auto-fit";
37 const std::string UNIT_AUTO_STRETCH = "auto-stretch";
38 const std::string TRIM_TEMPLATE = "$1$2";
39 const std::string INVALID_PATTERN = R"(((repeat)\(\s{0,}((auto-fill)|(auto-fit))\s{0,},))";
40 const std::string SIZE_PATTERN = "\\s{0,}[0-9]+([.]{1}[0-9]+){0,1}(px|%|vp){0,1}";
41 const std::string PREFIX_PATTERN = R"(\S{1,}(repeat)|(px|%|vp)\d{1,}|\)\d{1,})";
42 const std::regex UNIT_PIXEL_REGEX(R"(^([0-9]\d*|[0-9]\d*.\d*|0\.\d*[0-9]\d*)px$)", std::regex::icase);
43 const std::regex UNIT_RATIO_REGEX(R"(^([0-9]\d*|[0-9]\d*.\d*|0\.\d*[0-9]\d*)fr$)", std::regex::icase);
44 const std::regex UNIT_PERCENT_REGEX(R"(^([0-9]\d*|[0-9]\d*.\d*|0\.\d*[0-9]\d*)%$)", std::regex::icase);
45 const std::regex AUTO_REGEX(R"(^repeat\((.+),(.+)\))", std::regex::icase); // regex for "repeat(auto-fill, 10px)"
46 const std::regex REPEAT_NUM_REGEX(R"(^repeat\((\d+),(.+)\))", std::regex::icase); // regex for "repeat(2, 100px)"
47 const std::regex TRIM_REGEX(R"(^ +| +$|(\"[^\"\\\\]*(?:\\\\[\\s\\S][^\"\\\\]*)*\")|( ) +)", std::regex::icase);
48 const std::string REPEAT_WITH_AUTOFILL_OR_AUTOFIT =
49 R"(((repeat)\(\s{0,}((auto-fill)|(auto-fit))\s{0,},(\s{0,}[0-9]+([.]{1}[0-9]+){0,1}(px|%|vp){0,1}){1,}\s{0,}\)))";
50 const std::string AUTO_STRETCH_REGEX = R"(^\s*repeat\(auto-stretch,\s*(\d+(\.\d+)?(vp|px)?)\)$)";
51
52 enum class RepeatType {
53 NONE = 0,
54 FIXED_COUNT,
55 AUTO_FILL,
56 };
57
58 struct Repeat {
59 std::string str;
60 bool isRepeat = false;
61 };
62 using Value = Repeat;
63
ConvertVirtualSize(const std::string & val,const DimensionUnit & unit,double size)64 double ConvertVirtualSize(const std::string& val, const DimensionUnit& unit, double size)
65 {
66 double ret = StringUtils::StringToDouble(val);
67 switch (unit) {
68 case DimensionUnit::PERCENT:
69 ret = ret / FULL_PERCENT * size;
70 break;
71 case DimensionUnit::VP: {
72 auto pipelineContext = PipelineBase::GetCurrentContext();
73 if (pipelineContext) {
74 ret = pipelineContext->NormalizeToPx(Dimension(ret, DimensionUnit::VP));
75 } else {
76 ret = Dimension(ret, DimensionUnit::VP).Value();
77 }
78 }
79 break;
80 case DimensionUnit::PX:
81 break;
82 default:
83 ret = 0.0;
84 break;
85 }
86 return ret;
87 }
88
ParseUnit(const Value & val,double size)89 double ParseUnit(const Value& val, double size)
90 {
91 double ret = 0;
92 std::regex pattern(R"(^\s{0,}[0-9]+([.]{1}[0-9]+){0,1}\s{0,})");
93 std::smatch match;
94 if (val.str.find(UNIT_PIXEL) != std::string::npos) {
95 ret = ConvertVirtualSize(val.str, DimensionUnit::PX, size);
96 } else if (val.str.find(UNIT_PERCENT) != std::string::npos) {
97 ret = ConvertVirtualSize(val.str, DimensionUnit::PERCENT, size);
98 } else if (val.str.find(UNIT_VP) != std::string::npos || std::regex_match(val.str, match, pattern)) {
99 ret = ConvertVirtualSize(val.str, DimensionUnit::VP, size);
100 }
101 return ret;
102 }
103
TrimTemplate(std::string & str)104 std::string TrimTemplate(std::string& str)
105 {
106 return std::regex_replace(str, TRIM_REGEX, TRIM_TEMPLATE);
107 }
108
RTrim(std::string & str)109 void RTrim(std::string& str)
110 {
111 str.erase(std::find_if(str.rbegin(), str.rend(), [](int ch) { return !std::isspace(ch); }).base(), str.end());
112 }
113
SplitTemplate(const std::string & str,std::vector<Value> & vec,bool isRepeat=false)114 bool SplitTemplate(const std::string& str, std::vector<Value>& vec, bool isRepeat = false)
115 {
116 std::string merge;
117 std::string regexResult;
118 std::smatch result;
119 std::regex pattern(SIZE_PATTERN, std::regex::icase);
120 std::string::const_iterator iterStart = str.begin();
121 std::string::const_iterator iterEnd = str.end();
122
123 while (std::regex_search(iterStart, iterEnd, result, pattern)) {
124 regexResult = result[0];
125 merge += regexResult;
126 Value value;
127 value.str = TrimTemplate(regexResult);
128 value.isRepeat = isRepeat;
129 vec.emplace_back(value);
130 iterStart = result[0].second;
131 }
132 return (merge.length() == str.length());
133 }
134
GetRepeat(const std::string & str)135 std::string GetRepeat(const std::string& str)
136 {
137 std::smatch result;
138 std::regex pattern(REPEAT_WITH_AUTOFILL_OR_AUTOFIT, std::regex::icase);
139 std::string::const_iterator iterStart = str.begin();
140 std::string::const_iterator iterEnd = str.end();
141 std::string regexResult;
142 size_t count = 0;
143 while (std::regex_search(iterStart, iterEnd, result, pattern)) {
144 regexResult = result[0];
145 iterStart = result[0].second;
146 ++count;
147 }
148 if (count == 1) {
149 return regexResult;
150 }
151 return { "" };
152 }
153
CheckRepeatAndSplitString(std::vector<std::string> & vec,std::string & repeat,std::vector<Value> & resultvec)154 bool CheckRepeatAndSplitString(std::vector<std::string>& vec, std::string& repeat, std::vector<Value>& resultvec)
155 {
156 if (repeat.length() == 0 && vec.empty()) {
157 return false;
158 }
159 std::string regexResult;
160 std::regex pattern(INVALID_PATTERN, std::regex::icase);
161
162 for (auto it = vec.begin(); it != vec.end(); it++) {
163 RTrim(*it);
164 bool ret = SplitTemplate(*it, resultvec);
165 if (!ret) {
166 return ret;
167 }
168 if (it == vec.begin()) {
169 regexResult = std::regex_replace(repeat, pattern, "");
170 if (regexResult.length() == 0) {
171 return false;
172 }
173 regexResult.erase(regexResult.end() - 1);
174 RTrim(regexResult);
175 ret = SplitTemplate(regexResult, resultvec, true);
176 if (!ret) {
177 return ret;
178 }
179 }
180 }
181 return true;
182 }
183
CheckAutoFillParameter(const std::string & args,double size,std::vector<double> & out,std::vector<Value> & resultvec)184 bool CheckAutoFillParameter(
185 const std::string& args, double size, std::vector<double>& out, std::vector<Value>& resultvec)
186 {
187 out.clear();
188 if (args.empty()) {
189 return false;
190 }
191 std::smatch result;
192 std::regex patternFilter(PREFIX_PATTERN, std::regex::icase);
193 if (std::regex_search(args, result, patternFilter)) {
194 out.push_back(size);
195 return false;
196 }
197
198 std::regex pattern(REPEAT_WITH_AUTOFILL_OR_AUTOFIT, std::regex::icase);
199 std::vector<std::string> vec(
200 std::sregex_token_iterator(args.begin(), args.end(), pattern, -1), std::sregex_token_iterator());
201
202 std::string repeat = GetRepeat(args);
203 bool invalidRepeatAutoFill = CheckRepeatAndSplitString(vec, repeat, resultvec);
204 if (!invalidRepeatAutoFill && out.empty()) {
205 out.push_back(size);
206 }
207 return invalidRepeatAutoFill;
208 }
209
ParseArgsWithAutoFill(const std::string & args,double size,double gap,int32_t childrenCount)210 std::pair<std::vector<double>, double> ParseArgsWithAutoFill(
211 const std::string& args, double size, double gap, int32_t childrenCount)
212 {
213 std::vector<double> lens;
214 std::vector<Value> retTemplates;
215 if (!CheckAutoFillParameter(args, size, lens, retTemplates)) {
216 return std::make_pair(lens, gap);
217 }
218 int32_t countNonRepeat = 0;
219 int countRepeat = 0;
220 double sizeRepeat = 0.0;
221 double sizeNonRepeat = 0.0;
222 bool invalidRepeatAutoFill = false;
223 std::vector<double> prefixLens;
224 std::vector<double> repeatLens;
225 std::vector<double> suffixLens;
226 for (const auto& ret : retTemplates) {
227 double sizeItem = ParseUnit(ret, size);
228 if (ret.isRepeat) {
229 invalidRepeatAutoFill = true;
230 sizeRepeat += sizeItem;
231 ++countRepeat;
232 repeatLens.push_back(sizeItem);
233 } else {
234 ++countNonRepeat;
235 sizeNonRepeat += sizeItem;
236 if (invalidRepeatAutoFill) {
237 suffixLens.push_back(sizeItem);
238 } else {
239 prefixLens.push_back(sizeItem);
240 }
241 }
242 }
243 if (countNonRepeat + countRepeat > 0 && (countNonRepeat + countRepeat - 1) * gap > size) {
244 gap = 0.0;
245 }
246 double sizeNonRepeatGap = GreatNotEqual(countNonRepeat, 0) ? (countNonRepeat - 1) * gap : 0;
247 double sizeLeft = size - sizeNonRepeatGap - sizeNonRepeat;
248 double count = 0;
249 if (!NearZero(sizeRepeat)) {
250 count = (sizeLeft + gap) / (sizeRepeat + countRepeat * gap);
251 count = LessOrEqual(count, 1) ? 1 : floor(count);
252 } else {
253 if (childrenCount >= countNonRepeat && !retTemplates.empty()) {
254 count = ceil((size - countNonRepeat) * 1.0 / (static_cast<int32_t>(retTemplates.size()) - countNonRepeat));
255 }
256 }
257 lens.insert(lens.end(), prefixLens.begin(), prefixLens.end());
258 for (int i = 0; i < count; i++) {
259 lens.insert(lens.end(), repeatLens.begin(), repeatLens.end());
260 }
261 lens.insert(lens.end(), suffixLens.begin(), suffixLens.end());
262 return std::make_pair(lens, gap);
263 }
264
ConvertRepeatArgs(std::string & handledArg)265 void ConvertRepeatArgs(std::string& handledArg)
266 {
267 if (handledArg.find(REPEAT_PREFIX) == std::string::npos) {
268 return;
269 }
270 handledArg.erase(0, handledArg.find_first_not_of(' ')); // trim the input str
271 std::smatch matches;
272 if (handledArg.find(UNIT_AUTO_FILL) != std::string::npos) {
273 if (handledArg.size() > REPEAT_MIN_SIZE && std::regex_match(handledArg, matches, AUTO_REGEX)) {
274 handledArg = matches[1].str() + matches[CROSS_WIDTH].str();
275 }
276 } else {
277 if (handledArg.size() > REPEAT_MIN_SIZE && std::regex_match(handledArg, matches, REPEAT_NUM_REGEX)) {
278 auto count = StringUtils::StringToInt(matches[1].str());
279 std::string repeatString = matches[CROSS_WIDTH].str();
280 while (count > 1) {
281 repeatString.append(" " + std::string(matches[CROSS_WIDTH].str()));
282 --count;
283 }
284 handledArg = repeatString;
285 }
286 }
287 }
288
ParseAutoFill(const std::vector<std::string> & strs,double size,double gap)289 std::pair<std::vector<double>, double> ParseAutoFill(const std::vector<std::string>& strs, double size, double gap)
290 {
291 std::vector<double> lens;
292 if (strs.size() <= 1) {
293 return std::make_pair(lens, gap);
294 }
295 auto allocatedSize = size - (strs.size() - 2) * gap; // size() - 2 means 'auto-fill' should be erased.
296 if (allocatedSize > size) {
297 gap = 0.0;
298 }
299 double pxSum = 0.0;
300 double peSum = 0.0;
301 std::vector<double> newLens;
302 for (const auto& str : strs) {
303 auto num = StringUtils::StringToDouble(str);
304 if (str.find(UNIT_PIXEL) != std::string::npos) {
305 num = pxSum > allocatedSize ? 0.0 : num;
306 pxSum += num;
307 lens.emplace_back(num);
308 } else if (str.find(UNIT_PERCENT) != std::string::npos) {
309 // adjust invalid percent
310 num = peSum >= FULL_PERCENT ? 0.0 : num;
311 peSum += num;
312 pxSum += num / FULL_PERCENT * size;
313 lens.emplace_back(num / FULL_PERCENT * size);
314 } else {
315 LOGE("Unsupported type: %{public}s, and use 0.0", str.c_str());
316 return std::make_pair(newLens, gap);
317 }
318 }
319 allocatedSize -= pxSum;
320 if (LessOrEqual(allocatedSize, 0.0)) {
321 return std::make_pair(lens, gap);
322 }
323 pxSum += lens.size() * gap;
324 auto repeatCount = static_cast<int32_t>(allocatedSize / pxSum);
325 for (int32_t i = 0; i < repeatCount + 1; i++) {
326 newLens.insert(newLens.end(), lens.begin(), lens.end());
327 }
328 allocatedSize -= pxSum * repeatCount;
329 for (double& len : lens) {
330 allocatedSize -= len + gap;
331 if (LessNotEqual(allocatedSize, 0.0)) {
332 break;
333 }
334 newLens.emplace_back(len);
335 }
336 return std::make_pair(newLens, gap);
337 }
338
ParseArgsWithoutAutoFill(const std::string & args,double size,double gap)339 std::pair<std::vector<double>, double> ParseArgsWithoutAutoFill(const std::string& args, double size, double gap)
340 {
341 std::vector<double> lens;
342 if (args.empty()) {
343 return std::make_pair(lens, gap);
344 }
345 double pxSum = 0.0; // First priority: such as 50px
346 double peSum = 0.0; // Second priority: such as 20%
347 double frSum = 0.0; // Third priority: such as 2fr
348 std::vector<std::string> strs;
349 std::string handledArg = args;
350 ConvertRepeatArgs(handledArg);
351 StringUtils::StringSplitter(handledArg, ' ', strs);
352 if (!strs.empty() && strs[0] == UNIT_AUTO_FILL) {
353 return ParseAutoFill(strs, size, gap);
354 }
355 // first loop calculate all type sums.
356 for (const auto& str : strs) {
357 if (std::regex_match(str, UNIT_PIXEL_REGEX)) {
358 pxSum += StringUtils::StringToDouble(str);
359 } else if (std::regex_match(str, UNIT_PERCENT_REGEX)) {
360 peSum += StringUtils::StringToDouble(str);
361 } else if (std::regex_match(str, UNIT_RATIO_REGEX)) {
362 frSum += StringUtils::StringToDouble(str);
363 } else {
364 return std::make_pair(lens, gap);
365 }
366 }
367 peSum = GreatOrEqual(peSum, FULL_PERCENT) ? FULL_PERCENT : peSum;
368 // Second loop calculate actual width or height.
369 double sizeLeft = size > ((strs.size() - 1) * gap) ? size - (strs.size() - 1) * gap : size;
370 gap = size > ((strs.size() - 1) * gap) ? gap : 0.0;
371 double sizeNoGap = sizeLeft;
372 double prSumLeft = FULL_PERCENT;
373 double frSizeSum = sizeNoGap * (FULL_PERCENT - peSum) / FULL_PERCENT - pxSum;
374 for (const auto& str : strs) {
375 double num = StringUtils::StringToDouble(str);
376 if (str.find(UNIT_PIXEL) != std::string::npos) {
377 lens.push_back(sizeLeft < 0.0 ? 0.0 : std::clamp(num, 0.0, sizeLeft));
378 sizeLeft -= num;
379 } else if (str.find(UNIT_PERCENT) != std::string::npos) {
380 num = prSumLeft < num ? prSumLeft : num;
381 auto prSize = sizeNoGap * num / FULL_PERCENT;
382 lens.push_back(prSize);
383 prSumLeft -= num;
384 sizeLeft -= prSize;
385 } else if (str.find(UNIT_RATIO) != std::string::npos) {
386 frSum = LessNotEqual(frSum, 1) ? 1 : frSum;
387 lens.push_back(NearZero(frSum) ? 0.0 : frSizeSum / frSum * num);
388 }
389 }
390 return std::make_pair(lens, gap);
391 }
392
ParseArgsWithAutoFit(const std::string & args,double size,double gap,int32_t childrenCount)393 std::pair<std::vector<double>, double> ParseArgsWithAutoFit(
394 const std::string& args, double size, double gap, int32_t childrenCount)
395 {
396 std::vector<double> lens;
397 std::vector<Value> retTemplates;
398 if (!CheckAutoFillParameter(args, size, lens, retTemplates)) {
399 return std::make_pair(lens, gap);
400 }
401
402 size_t countNonRepeat = 0;
403 int countRepeat = 0;
404 double sizeRepeat = 0.0;
405 double sizeNonRepeat = 0.0;
406 bool invalidRepeatAutoFill = false;
407
408 std::vector<double> prefixLens;
409 std::vector<double> repeatLens;
410 std::vector<double> suffixLens;
411
412 for (const auto& ret : retTemplates) {
413 double sizeItem = ParseUnit(ret, size);
414 if (ret.isRepeat) {
415 invalidRepeatAutoFill = true;
416 sizeRepeat += sizeItem;
417 ++countRepeat;
418 repeatLens.push_back(sizeItem);
419 } else {
420 ++countNonRepeat;
421 sizeNonRepeat += sizeItem;
422 if (invalidRepeatAutoFill) {
423 suffixLens.push_back(sizeItem);
424 } else {
425 prefixLens.push_back(sizeItem);
426 }
427 }
428 }
429
430 double sizeNonRepeatGap = GreatNotEqual(countNonRepeat, 0) ? (countNonRepeat - 1) * gap : 0;
431 double sizeLeft = size - sizeNonRepeatGap - sizeNonRepeat;
432 double count = 0;
433 if (!NearZero(sizeRepeat)) {
434 count = (sizeLeft + gap) / (sizeRepeat + countRepeat * gap);
435 count = LessOrEqual(count, 1) ? 1 : floor(count);
436 } else {
437 if (childrenCount >= static_cast<int32_t>(countNonRepeat) && !retTemplates.empty()) {
438 count = ceil((size - countNonRepeat) * 1.0 / (retTemplates.size() - countNonRepeat));
439 }
440 }
441 if (!NearZero(count)) {
442 double gridWidth = (sizeLeft + gap) / count - gap;
443 lens.insert(lens.end(), prefixLens.begin(), prefixLens.end());
444 for (int i = 0; i < count; i++) {
445 lens.push_back(gridWidth);
446 }
447 lens.insert(lens.end(), suffixLens.begin(), suffixLens.end());
448 }
449
450 return std::make_pair(lens, gap);
451 }
452
CheckAutoStretchParameter(const std::string & args,Value & track)453 bool CheckAutoStretchParameter(const std::string& args, Value& track)
454 {
455 std::regex pattern(AUTO_STRETCH_REGEX, std::regex::icase);
456 std::smatch match;
457 if (std::regex_match(args, match, pattern)) {
458 if (match.size() == STRETCH_MATCH_SIZE) {
459 track.str = match[1].str();
460 return true;
461 }
462 }
463 return false;
464 }
465
ParseArgsWithAutoStretch(const std::string & args,double size,double gap)466 std::pair<std::vector<double>, double> ParseArgsWithAutoStretch(const std::string& args, double size, double gap)
467 {
468 std::vector<double> lens;
469 Value track;
470 if (!CheckAutoStretchParameter(args, track)) {
471 return std::make_pair(lens, gap);
472 }
473
474 double trackSize = ParseUnit(track, size);
475 float realGap = gap;
476 int32_t columnCount = 1;
477 if (trackSize + gap > 0) {
478 columnCount = std::floor((size + gap) / (trackSize + gap));
479 columnCount = std::max(columnCount, 1);
480 if (columnCount > 1) {
481 realGap = (size - columnCount * trackSize) / (columnCount - 1);
482 }
483 }
484
485 for (int32_t i = 0; i < columnCount; i++) {
486 lens.emplace_back(trackSize);
487 }
488
489 return std::make_pair(lens, realGap);
490 }
491 } // namespace
492
ParseTemplateArgs(const std::string & args,double size,double gap,int32_t childrenCount)493 std::pair<std::vector<double>, double> ParseTemplateArgs(
494 const std::string& args, double size, double gap, int32_t childrenCount)
495 {
496 if (args.find(REPEAT_PREFIX) != std::string::npos && args.find(UNIT_AUTO_FILL) != std::string::npos) {
497 return ParseArgsWithAutoFill(args, size, gap, childrenCount);
498 }
499 if (args.find(REPEAT_PREFIX) != std::string::npos && args.find(UNIT_AUTO_FIT) != std::string::npos) {
500 return ParseArgsWithAutoFit(args, size, gap, childrenCount);
501 }
502 if (args.find(REPEAT_PREFIX) != std::string::npos && args.find(UNIT_AUTO_STRETCH) != std::string::npos) {
503 return ParseArgsWithAutoStretch(args, size, gap);
504 }
505 return NG::ParseArgsWithoutAutoFill(args, size, gap);
506 }
507 } // namespace OHOS::Ace::NG
508