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