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