• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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 "json_compiler.h"
17 #include <iostream>
18 #include <limits>
19 #include <regex>
20 #include "restool_errors.h"
21 #include "translatable_parser.h"
22 
23 namespace OHOS {
24 namespace Global {
25 namespace Restool {
26 using namespace std;
27 const string TAG_NAME = "name";
28 const string TAG_VALUE = "value";
29 const string TAG_PARENT = "parent";
30 const string TAG_QUANTITY = "quantity";
31 const vector<string> QUANTITY_ATTRS = { "zero", "one", "two", "few", "many", "other" };
32 const vector<string> TRANSLATION_TYPE = { "string", "strarray", "plural" };
33 
JsonCompiler(ResType type,const string & output,bool isOverlap)34 JsonCompiler::JsonCompiler(ResType type, const string &output, bool isOverlap)
35     : IResourceCompiler(type, output, isOverlap), isBaseString_(false), root_(nullptr)
36 {
37     InitParser();
38 }
39 
~JsonCompiler()40 JsonCompiler::~JsonCompiler()
41 {
42     if (root_) {
43         cJSON_Delete(root_);
44     }
45 }
46 
CompileSingleFile(const FileInfo & fileInfo)47 uint32_t JsonCompiler::CompileSingleFile(const FileInfo &fileInfo)
48 {
49     if (fileInfo.limitKey == "base" &&
50         fileInfo.fileCluster == "element" &&
51         fileInfo.filename == ID_DEFINED_FILE) {
52         return RESTOOL_SUCCESS;
53     }
54 
55     if (!ResourceUtil::OpenJsonFile(fileInfo.filePath, &root_)) {
56         return RESTOOL_ERROR;
57     }
58     if (!root_ || !cJSON_IsObject(root_)) {
59         PrintError(GetError(ERR_CODE_JSON_FORMAT_ERROR).SetPosition(fileInfo.filePath));
60         return RESTOOL_ERROR;
61     }
62     cJSON *item = root_->child;
63     if (cJSON_GetArraySize(root_) != 1) {
64         PrintError(GetError(ERR_CODE_JSON_NOT_ONE_MEMBER).FormatCause("root").SetPosition(fileInfo.filePath));
65         return RESTOOL_ERROR;
66     }
67 
68     string tag = item->string;
69     auto ret = g_contentClusterMap.find(tag);
70     if (ret == g_contentClusterMap.end()) {
71         PrintError(GetError(ERR_CODE_JSON_INVALID_NODE_NAME)
72                        .FormatCause(tag.c_str(), ResourceUtil::GetAllRestypeString().c_str())
73                        .SetPosition(fileInfo.filePath));
74         return RESTOOL_ERROR;
75     }
76     isBaseString_ = (fileInfo.limitKey == "base" &&
77         find(TRANSLATION_TYPE.begin(), TRANSLATION_TYPE.end(), tag) != TRANSLATION_TYPE.end());
78     FileInfo copy = fileInfo;
79     copy.fileType = ret->second;
80     if (!ParseJsonArrayLevel(item, copy)) {
81         return RESTOOL_ERROR;
82     }
83     return RESTOOL_SUCCESS;
84 }
85 
86 // below private
InitParser()87 void JsonCompiler::InitParser()
88 {
89     using namespace placeholders;
90     handles_.emplace(ResType::STRING, bind(&JsonCompiler::HandleString, this, _1, _2));
91     handles_.emplace(ResType::INTEGER, bind(&JsonCompiler::HandleInteger, this, _1, _2));
92     handles_.emplace(ResType::BOOLEAN, bind(&JsonCompiler::HandleBoolean, this, _1, _2));
93     handles_.emplace(ResType::COLOR, bind(&JsonCompiler::HandleColor, this, _1, _2));
94     handles_.emplace(ResType::FLOAT, bind(&JsonCompiler::HandleFloat, this, _1, _2));
95     handles_.emplace(ResType::STRARRAY, bind(&JsonCompiler::HandleStringArray, this, _1, _2));
96     handles_.emplace(ResType::INTARRAY, bind(&JsonCompiler::HandleIntegerArray, this, _1, _2));
97     handles_.emplace(ResType::THEME, bind(&JsonCompiler::HandleTheme, this, _1, _2));
98     handles_.emplace(ResType::PATTERN, bind(&JsonCompiler::HandlePattern, this, _1, _2));
99     handles_.emplace(ResType::PLURAL, bind(&JsonCompiler::HandlePlural, this, _1, _2));
100     handles_.emplace(ResType::SYMBOL, bind(&JsonCompiler::HandleSymbol, this, _1, _2));
101 }
102 
ParseJsonArrayLevel(const cJSON * arrayNode,const FileInfo & fileInfo)103 bool JsonCompiler::ParseJsonArrayLevel(const cJSON *arrayNode, const FileInfo &fileInfo)
104 {
105     if (!arrayNode || !cJSON_IsArray(arrayNode)) {
106         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
107                        .FormatCause(ResourceUtil::ResTypeToString(fileInfo.fileType).c_str(), "array")
108                        .SetPosition(fileInfo.filePath));
109         return false;
110     }
111 
112     if (cJSON_GetArraySize(arrayNode) == 0) {
113         PrintError(GetError(ERR_CODE_JSON_NODE_EMPTY)
114                        .FormatCause(ResourceUtil::ResTypeToString(fileInfo.fileType).c_str())
115                        .SetPosition(fileInfo.filePath));
116         return false;
117     }
118     int32_t index = -1;
119     for (cJSON *item = arrayNode->child; item; item = item->next) {
120         index++;
121         if (!item || !cJSON_IsObject(item)) {
122             PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("item", "object")
123                 .SetPosition(fileInfo.filePath));
124             return false;
125         }
126         if (!ParseJsonObjectLevel(item, fileInfo)) {
127             return false;
128         }
129     }
130     return true;
131 }
132 
ParseJsonObjectLevel(cJSON * objectNode,const FileInfo & fileInfo)133 bool JsonCompiler::ParseJsonObjectLevel(cJSON *objectNode, const FileInfo &fileInfo)
134 {
135     cJSON *nameNode = cJSON_GetObjectItem(objectNode, TAG_NAME.c_str());
136     if (!nameNode) {
137         PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause("name").SetPosition(fileInfo.filePath));
138         return false;
139     }
140 
141     if (!cJSON_IsString(nameNode)) {
142         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause(TAG_NAME.c_str(), "string")
143             .SetPosition(fileInfo.filePath));
144         return false;
145     }
146 
147     if (isBaseString_ && !TranslatableParse::ParseTranslatable(objectNode, fileInfo, nameNode->valuestring)) {
148         return false;
149     }
150     ResourceItem resourceItem(nameNode->valuestring, fileInfo.keyParams, fileInfo.fileType);
151     resourceItem.SetFilePath(fileInfo.filePath);
152     resourceItem.SetLimitKey(fileInfo.limitKey);
153     auto ret = handles_.find(fileInfo.fileType);
154     if (ret == handles_.end()) {
155         std::string elementTypes = "[";
156         for (const auto &handle : handles_) {
157             elementTypes.append("\"").append(ResourceUtil::ResTypeToString(handle.first)).append("\",");
158         }
159         elementTypes.pop_back();
160         elementTypes.append("]");
161         PrintError(GetError(ERR_CODE_INVALID_ELEMENT_TYPE)
162                        .FormatCause(ResourceUtil::ResTypeToString(fileInfo.fileType).c_str(), elementTypes.c_str())
163                        .SetPosition(fileInfo.filePath));
164         return false;
165     }
166 
167     if (!ret->second(objectNode, resourceItem)) {
168         return false;
169     }
170 
171     if (isOverlap_) {
172         resourceItem.MarkCoverable();
173     }
174 
175     return MergeResourceItem(resourceItem);
176 }
177 
HandleString(const cJSON * objectNode,ResourceItem & resourceItem) const178 bool JsonCompiler::HandleString(const cJSON *objectNode, ResourceItem &resourceItem) const
179 {
180     cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
181     if (!CheckJsonStringValue(valueNode, resourceItem)) {
182         return false;
183     }
184     return PushString(valueNode->valuestring, resourceItem);
185 }
186 
HandleInteger(const cJSON * objectNode,ResourceItem & resourceItem) const187 bool JsonCompiler::HandleInteger(const cJSON *objectNode, ResourceItem &resourceItem) const
188 {
189     cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
190     if (!CheckJsonIntegerValue(valueNode, resourceItem)) {
191         return false;
192     }
193     if (cJSON_IsString(valueNode)) {
194         return PushString(valueNode->valuestring, resourceItem);
195     } else if (cJSON_IsNumber(valueNode)) {
196         return PushString(to_string(valueNode->valueint), resourceItem);
197     } else {
198         return false;
199     }
200 }
201 
HandleBoolean(const cJSON * objectNode,ResourceItem & resourceItem) const202 bool JsonCompiler::HandleBoolean(const cJSON *objectNode, ResourceItem &resourceItem) const
203 {
204     cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
205     if (valueNode == nullptr) {
206         PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause(string(resourceItem.GetName() + " value").c_str())
207             .SetPosition(resourceItem.GetFilePath()));
208         return false;
209     }
210     if (cJSON_IsString(valueNode)) {
211         regex ref("^\\$(ohos:)?boolean:.*");
212         if (!regex_match(valueNode->valuestring, ref)) {
213             PrintError(GetError(ERR_CODE_INVALID_RESOURCE_REF).FormatCause(valueNode->valuestring, "$(ohos:)?boolean:")
214                 .SetPosition(resourceItem.GetFilePath()));
215             return false;
216         }
217         return PushString(valueNode->valuestring, resourceItem);
218     }
219     if (!cJSON_IsBool(valueNode)) {
220         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
221                        .FormatCause(string(resourceItem.GetName() + " value").c_str(), "bool")
222                        .SetPosition(resourceItem.GetFilePath()));
223         return false;
224     }
225     return PushString(cJSON_IsTrue(valueNode) == 1 ? "true" : "false", resourceItem);
226 }
227 
HandleColor(const cJSON * objectNode,ResourceItem & resourceItem) const228 bool JsonCompiler::HandleColor(const cJSON *objectNode, ResourceItem &resourceItem) const
229 {
230     return HandleString(objectNode, resourceItem);
231 }
232 
HandleFloat(const cJSON * objectNode,ResourceItem & resourceItem) const233 bool JsonCompiler::HandleFloat(const cJSON *objectNode, ResourceItem &resourceItem) const
234 {
235     return HandleString(objectNode, resourceItem);
236 }
237 
HandleStringArray(const cJSON * objectNode,ResourceItem & resourceItem) const238 bool JsonCompiler::HandleStringArray(const cJSON *objectNode, ResourceItem &resourceItem) const
239 {
240     vector<string> extra;
241     return ParseValueArray(objectNode, resourceItem, extra,
242         [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) -> bool {
243             if (!cJSON_IsObject(arrayItem)) {
244                 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
245                                .FormatCause(string(resourceItem.GetName() + " value").c_str(), "object")
246                                .SetPosition(resourceItem.GetFilePath()));
247                 return false;
248             }
249             cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
250             if (!CheckJsonStringValue(valueNode, resourceItem)) {
251                 return false;
252             }
253             values.push_back(valueNode->valuestring);
254             return true;
255     });
256 }
257 
HandleIntegerArray(const cJSON * objectNode,ResourceItem & resourceItem) const258 bool JsonCompiler::HandleIntegerArray(const cJSON *objectNode, ResourceItem &resourceItem) const
259 {
260     vector<string> extra;
261     return ParseValueArray(objectNode, resourceItem, extra,
262         [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) -> bool {
263             if (!CheckJsonIntegerValue(arrayItem, resourceItem)) {
264                 return false;
265             }
266             if (cJSON_IsString(arrayItem)) {
267                 values.push_back(arrayItem->valuestring);
268             } else {
269                 values.push_back(to_string(arrayItem->valueint));
270             }
271             return true;
272     });
273 }
274 
HandleTheme(const cJSON * objectNode,ResourceItem & resourceItem) const275 bool JsonCompiler::HandleTheme(const cJSON *objectNode, ResourceItem &resourceItem) const
276 {
277     vector<string> extra;
278     if (!ParseParent(objectNode, resourceItem, extra)) {
279         return false;
280     }
281     return ParseValueArray(objectNode, resourceItem, extra,
282         [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) {
283             return ParseAttribute(arrayItem, resourceItem, values);
284         });
285 }
286 
HandlePattern(const cJSON * objectNode,ResourceItem & resourceItem) const287 bool JsonCompiler::HandlePattern(const cJSON *objectNode, ResourceItem &resourceItem) const
288 {
289     return HandleTheme(objectNode, resourceItem);
290 }
291 
HandlePlural(const cJSON * objectNode,ResourceItem & resourceItem) const292 bool JsonCompiler::HandlePlural(const cJSON *objectNode, ResourceItem &resourceItem) const
293 {
294     vector<string> extra;
295     vector<string> attrs;
296     bool result = ParseValueArray(objectNode, resourceItem, extra,
297         [&attrs, this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) {
298             if (!CheckPluralValue(arrayItem, resourceItem)) {
299                 return false;
300             }
301             cJSON *quantityNode = cJSON_GetObjectItem(arrayItem, TAG_QUANTITY.c_str());
302             if (!quantityNode || !cJSON_IsString(quantityNode)) {
303                 return false;
304             }
305             string quantityValue = quantityNode->valuestring;
306             if (find(attrs.begin(), attrs.end(), quantityValue) != attrs.end()) {
307                 PrintError(GetError(ERR_CODE_DUPLICATE_QUANTITY)
308                                .FormatCause(quantityValue.c_str(), resourceItem.GetName().c_str())
309                                .SetPosition(resourceItem.GetFilePath()));
310                 return false;
311             }
312             attrs.push_back(quantityValue);
313             values.push_back(quantityValue);
314             cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
315             if (!valueNode || !cJSON_IsString(valueNode)) {
316                 return false;
317             }
318             values.push_back(valueNode->valuestring);
319             return true;
320         });
321     if (!result) {
322         return false;
323     }
324     if (find(attrs.begin(), attrs.end(), "other") == attrs.end()) {
325         PrintError(GetError(ERR_CODE_QUANTITY_NO_OTHER).FormatCause(resourceItem.GetName().c_str())
326             .SetPosition(resourceItem.GetFilePath()));
327         return false;
328     }
329     return true;
330 }
331 
HandleSymbol(const cJSON * objectNode,ResourceItem & resourceItem) const332 bool JsonCompiler::HandleSymbol(const cJSON *objectNode, ResourceItem &resourceItem) const
333 {
334     cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
335     if (!CheckJsonSymbolValue(valueNode, resourceItem)) {
336         return false;
337     }
338     return PushString(valueNode->valuestring, resourceItem);
339 }
340 
PushString(const string & value,ResourceItem & resourceItem) const341 bool JsonCompiler::PushString(const string &value, ResourceItem &resourceItem) const
342 {
343     if (!resourceItem.SetData(reinterpret_cast<const int8_t *>(value.c_str()), value.length())) {
344         std::string msg = "item data is null, resource name: " + resourceItem.GetName();
345         PrintError(GetError(ERR_CODE_UNDEFINED_ERROR).FormatCause(msg.c_str()).SetPosition(resourceItem.GetFilePath()));
346         return false;
347     }
348     return true;
349 }
350 
CheckJsonStringValue(const cJSON * valueNode,const ResourceItem & resourceItem) const351 bool JsonCompiler::CheckJsonStringValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
352 {
353     if (!valueNode || !cJSON_IsString(valueNode)) {
354         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
355                        .FormatCause(string(resourceItem.GetName() + " value").c_str(), "string")
356                        .SetPosition(resourceItem.GetFilePath()));
357         return false;
358     }
359 
360     const map<ResType, string> REFS = {
361         { ResType::STRING, "\\$(ohos:)?string:" },
362         { ResType::STRARRAY, "\\$(ohos:)?string:" },
363         { ResType::COLOR, "\\$(ohos:)?color:" },
364         { ResType::FLOAT, "\\$(ohos:)?float:" }
365     };
366 
367     string value = valueNode->valuestring;
368     ResType type = resourceItem.GetResType();
369     if (type ==  ResType::COLOR && !CheckColorValue(value.c_str())) {
370         PrintError(GetError(ERR_CODE_INVALID_COLOR_VALUE).FormatCause(value.c_str(), resourceItem.GetName().c_str())
371             .SetPosition(resourceItem.GetFilePath()));
372         return false;
373     }
374     regex ref("^\\$.+:");
375     smatch result;
376     if (regex_search(value, result, ref) && !regex_match(result[0].str(), regex(REFS.at(type)))) {
377         PrintError(GetError(ERR_CODE_INVALID_RESOURCE_REF).FormatCause(value.c_str(), REFS.at(type).c_str())
378             .SetPosition(resourceItem.GetFilePath()));
379         return false;
380     }
381     return true;
382 }
383 
CheckJsonIntegerValue(const cJSON * valueNode,const ResourceItem & resourceItem) const384 bool JsonCompiler::CheckJsonIntegerValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
385 {
386     if (!valueNode) {
387         PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause(string(resourceItem.GetName() + " value").c_str())
388             .SetPosition(resourceItem.GetFilePath()));
389         return false;
390     }
391     if (cJSON_IsString(valueNode)) {
392         regex ref("^\\$(ohos:)?integer:.*");
393         if (!regex_match(valueNode->valuestring, ref)) {
394             PrintError(GetError(ERR_CODE_INVALID_RESOURCE_REF).FormatCause(valueNode->valuestring, "$(ohos:)?integer:")
395                 .SetPosition(resourceItem.GetFilePath()));
396             return false;
397         }
398     } else if (!ResourceUtil::IsIntValue(valueNode)) {
399         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
400                        .FormatCause(string(resourceItem.GetName() + " value").c_str(), "integer")
401                        .SetPosition(resourceItem.GetFilePath()));
402         return false;
403     }
404     return true;
405 }
406 
CheckJsonSymbolValue(const cJSON * valueNode,const ResourceItem & resourceItem) const407 bool JsonCompiler::CheckJsonSymbolValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
408 {
409     if (!valueNode || !cJSON_IsString(valueNode)) {
410         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
411                        .FormatCause(string(resourceItem.GetName() + " value").c_str(), "string")
412                        .SetPosition(resourceItem.GetFilePath()));
413         return false;
414     }
415     string unicodeStr = valueNode->valuestring;
416     if (regex_match(unicodeStr, regex("^\\$(ohos:)?symbol:.*"))) {
417         return true;
418     }
419     int unicode = strtol(unicodeStr.c_str(), nullptr, 16);
420     if (!ResourceUtil::isUnicodeInPlane15or16(unicode)) {
421         PrintError(GetError(ERR_CODE_INVALID_SYMBOL).FormatCause(unicode, resourceItem.GetName().c_str())
422             .SetPosition(resourceItem.GetFilePath()));
423         return false;
424     }
425     return true;
426 }
427 
ParseValueArray(const cJSON * objectNode,ResourceItem & resourceItem,const vector<string> & extra,HandleValue callback) const428 bool JsonCompiler::ParseValueArray(const cJSON *objectNode, ResourceItem &resourceItem,
429                                    const vector<string> &extra, HandleValue callback) const
430 {
431     cJSON *arrayNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
432     if (arrayNode == nullptr) {
433         PrintError(GetError(ERR_CODE_JSON_NODE_MISSING)
434                        .FormatCause(string(resourceItem.GetName() + " value").c_str())
435                        .SetPosition(resourceItem.GetFilePath()));
436         return false;
437     }
438 
439     if (!cJSON_IsArray(arrayNode)) {
440         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
441                        .FormatCause(string(resourceItem.GetName() + " value").c_str(), "array")
442                        .SetPosition(resourceItem.GetFilePath()));
443         return false;
444     }
445 
446     if (cJSON_GetArraySize(arrayNode) == 0) {
447         PrintError(GetError(ERR_CODE_JSON_NODE_EMPTY)
448                        .FormatCause(string(resourceItem.GetName() + " value").c_str())
449                        .SetPosition(resourceItem.GetFilePath()));
450         return false;
451     }
452 
453     vector<string> contents;
454     if (!extra.empty()) {
455         contents.assign(extra.begin(), extra.end());
456     }
457     for (cJSON *item = arrayNode->child; item; item = item->next) {
458         vector<string> values;
459         if (!callback(item, resourceItem, values)) {
460             return false;
461         }
462         contents.insert(contents.end(), values.begin(), values.end());
463     }
464 
465     string data = ResourceUtil::ComposeStrings(contents);
466     if (data.empty()) {
467         PrintError(GetError(ERR_CODE_ARRAY_TOO_LARGE).FormatCause(resourceItem.GetName().c_str())
468             .SetPosition(resourceItem.GetFilePath()));
469         return false;
470     }
471     return PushString(data, resourceItem);
472 }
473 
ParseParent(const cJSON * objectNode,const ResourceItem & resourceItem,vector<string> & extra) const474 bool JsonCompiler::ParseParent(const cJSON *objectNode, const ResourceItem &resourceItem,
475                                vector<string> &extra) const
476 {
477     cJSON *parentNode = cJSON_GetObjectItem(objectNode, TAG_PARENT.c_str());
478     string type = ResourceUtil::ResTypeToString(resourceItem.GetResType());
479     if (parentNode) {
480         if (!cJSON_IsString(parentNode)) {
481             PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
482                            .FormatCause(string(resourceItem.GetName() + " parent").c_str(), "string")
483                            .SetPosition(resourceItem.GetFilePath()));
484             return false;
485         }
486         string parentValue = parentNode->valuestring;
487         if (parentValue.empty()) {
488             PrintError(GetError(ERR_CODE_PARENT_EMPTY).FormatCause(resourceItem.GetName().c_str())
489                 .SetPosition(resourceItem.GetFilePath()));
490             return false;
491         }
492         if (regex_match(parentValue, regex("^ohos:" + type + ":.+"))) {
493             parentValue = "$" + parentValue;
494         } else {
495             parentValue = "$" + type + ":" + parentValue;
496         }
497         extra.push_back(parentValue);
498     }
499     return true;
500 }
501 
ParseAttribute(const cJSON * arrayItem,const ResourceItem & resourceItem,vector<string> & values) const502 bool JsonCompiler::ParseAttribute(const cJSON *arrayItem, const ResourceItem &resourceItem,
503                                   vector<string> &values) const
504 {
505     string type = ResourceUtil::ResTypeToString(resourceItem.GetResType());
506     string nodeAttr = string(resourceItem.GetName() + " attribute");
507     string filePath = resourceItem.GetFilePath();
508     if (!arrayItem || !cJSON_IsObject(arrayItem)) {
509         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause(nodeAttr.c_str(), "object")
510             .SetPosition(filePath));
511         return false;
512     }
513     cJSON *nameNode = cJSON_GetObjectItem(arrayItem, TAG_NAME.c_str());
514     if (!nameNode) {
515         PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause(nodeAttr.c_str()).SetPosition(filePath));
516         return false;
517     }
518     if (!cJSON_IsString(nameNode)) {
519         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause(string(nodeAttr + " name").c_str(), "string")
520             .SetPosition(filePath));
521         return false;
522     }
523     values.push_back(nameNode->valuestring);
524 
525     cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
526     if (!valueNode) {
527         PrintError(GetError(ERR_CODE_JSON_NODE_MISSING)
528                        .FormatCause(string(nodeAttr + " '" + nameNode->valuestring + "'").c_str())
529                        .SetPosition(filePath));
530         return false;
531     }
532     if (!cJSON_IsString(valueNode)) {
533         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
534                        .FormatCause(string(nodeAttr + " '" + nameNode->valuestring + "'").c_str(), "object")
535                        .SetPosition(filePath));
536         return false;
537     }
538     values.push_back(valueNode->valuestring);
539     return true;
540 }
541 
CheckPluralValue(const cJSON * arrayItem,const ResourceItem & resourceItem) const542 bool JsonCompiler::CheckPluralValue(const cJSON *arrayItem, const ResourceItem &resourceItem) const
543 {
544     string filePath = resourceItem.GetFilePath();
545     if (!arrayItem || !cJSON_IsObject(arrayItem)) {
546         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
547                        .FormatCause(string(resourceItem.GetName() + " array item").c_str(), "object")
548                        .SetPosition(filePath));
549         return false;
550     }
551     cJSON *quantityNode = cJSON_GetObjectItem(arrayItem, TAG_QUANTITY.c_str());
552     if (!quantityNode) {
553         PrintError(GetError(ERR_CODE_JSON_NODE_MISSING)
554                        .FormatCause(string(resourceItem.GetName() + " quantity").c_str())
555                        .SetPosition(filePath));
556         return false;
557     }
558     if (!cJSON_IsString(quantityNode)) {
559         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
560                        .FormatCause(string(resourceItem.GetName() + " quantity").c_str(), "string")
561                        .SetPosition(filePath));
562         return false;
563     }
564     string quantityValue = quantityNode->valuestring;
565     if (find(QUANTITY_ATTRS.begin(), QUANTITY_ATTRS.end(), quantityValue) == QUANTITY_ATTRS.end()) {
566         string buffer("[");
567         for_each(QUANTITY_ATTRS.begin(), QUANTITY_ATTRS.end(), [&buffer](auto iter) {
568             buffer.append("\"").append(iter).append("\",");
569         });
570         buffer.pop_back();
571         buffer.append("]");
572         PrintError(GetError(ERR_CODE_INVALID_QUANTITY)
573                        .FormatCause(quantityValue.c_str(), resourceItem.GetName().c_str(), buffer.c_str())
574                        .SetPosition(filePath));
575         return false;
576     }
577 
578     cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
579     if (!valueNode) {
580         PrintError(GetError(ERR_CODE_JSON_NODE_MISSING)
581                        .FormatCause(string(resourceItem.GetName() + " '" + quantityValue + "' value").c_str())
582                        .SetPosition(filePath));
583         return false;
584     }
585     if (!cJSON_IsString(valueNode)) {
586         PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH)
587                        .FormatCause(string(resourceItem.GetName() + " '" + quantityValue + "' value").c_str(), "string")
588                        .SetPosition(filePath));
589         return false;
590     }
591     return true;
592 }
593 
CheckColorValue(const char * s) const594 bool JsonCompiler::CheckColorValue(const char *s) const
595 {
596     if (s == nullptr) {
597         return false;
598     }
599     // color regex
600     string regColor = "^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$";
601     if (regex_match(s, regex("^\\$.*")) || regex_match(s, regex(regColor))) {
602         return true;
603     }
604     return false;
605 }
606 }
607 }
608 }
609