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)34 JsonCompiler::JsonCompiler(ResType type, const string &output)
35 : IResourceCompiler(type, output), 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 cerr << "Error: JSON file parsing failed, please check the JSON file.";
60 cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
61 return RESTOOL_ERROR;
62 }
63 cJSON *item = root_->child;
64 if (cJSON_GetArraySize(root_) != 1) {
65 cerr << "Error: node of a JSON file can only have one member, please check the JSON file.";
66 cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
67 return RESTOOL_ERROR;
68 }
69
70 string tag = item->string;
71 auto ret = g_contentClusterMap.find(tag);
72 if (ret == g_contentClusterMap.end()) {
73 cerr << "Error: invalid tag name '" << tag << "', please check the JSON file.";
74 cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
75 return RESTOOL_ERROR;
76 }
77 isBaseString_ = (fileInfo.limitKey == "base" &&
78 find(TRANSLATION_TYPE.begin(), TRANSLATION_TYPE.end(), tag) != TRANSLATION_TYPE.end());
79 FileInfo copy = fileInfo;
80 copy.fileType = ret->second;
81 if (!ParseJsonArrayLevel(item, copy)) {
82 return RESTOOL_ERROR;
83 }
84 return RESTOOL_SUCCESS;
85 }
86
87 // below private
InitParser()88 void JsonCompiler::InitParser()
89 {
90 using namespace placeholders;
91 handles_.emplace(ResType::STRING, bind(&JsonCompiler::HandleString, this, _1, _2));
92 handles_.emplace(ResType::INTEGER, bind(&JsonCompiler::HandleInteger, this, _1, _2));
93 handles_.emplace(ResType::BOOLEAN, bind(&JsonCompiler::HandleBoolean, this, _1, _2));
94 handles_.emplace(ResType::COLOR, bind(&JsonCompiler::HandleColor, this, _1, _2));
95 handles_.emplace(ResType::FLOAT, bind(&JsonCompiler::HandleFloat, this, _1, _2));
96 handles_.emplace(ResType::STRARRAY, bind(&JsonCompiler::HandleStringArray, this, _1, _2));
97 handles_.emplace(ResType::INTARRAY, bind(&JsonCompiler::HandleIntegerArray, this, _1, _2));
98 handles_.emplace(ResType::THEME, bind(&JsonCompiler::HandleTheme, this, _1, _2));
99 handles_.emplace(ResType::PATTERN, bind(&JsonCompiler::HandlePattern, this, _1, _2));
100 handles_.emplace(ResType::PLURAL, bind(&JsonCompiler::HandlePlural, this, _1, _2));
101 handles_.emplace(ResType::SYMBOL, bind(&JsonCompiler::HandleSymbol, this, _1, _2));
102 }
103
ParseJsonArrayLevel(const cJSON * arrayNode,const FileInfo & fileInfo)104 bool JsonCompiler::ParseJsonArrayLevel(const cJSON *arrayNode, const FileInfo &fileInfo)
105 {
106 if (!arrayNode || !cJSON_IsArray(arrayNode)) {
107 cerr << "Error: '" << ResourceUtil::ResTypeToString(fileInfo.fileType) << "' must be array.";
108 cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
109 return false;
110 }
111
112 if (cJSON_GetArraySize(arrayNode) == 0) {
113 cerr << "Error: '" << ResourceUtil::ResTypeToString(fileInfo.fileType) << "' empty.";
114 cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
115 return false;
116 }
117 int32_t index = -1;
118 for (cJSON *item = arrayNode->child; item; item = item->next) {
119 index++;
120 if (!item || !cJSON_IsObject(item)) {
121 cerr << "Error: the seq=" << index << " item must be object." << NEW_LINE_PATH << fileInfo.filePath << endl;
122 return false;
123 }
124 if (!ParseJsonObjectLevel(item, fileInfo)) {
125 return false;
126 }
127 }
128 return true;
129 }
130
ParseJsonObjectLevel(cJSON * objectNode,const FileInfo & fileInfo)131 bool JsonCompiler::ParseJsonObjectLevel(cJSON *objectNode, const FileInfo &fileInfo)
132 {
133 cJSON *nameNode = cJSON_GetObjectItem(objectNode, TAG_NAME.c_str());
134 if (!nameNode) {
135 cerr << "Error: name empty." << NEW_LINE_PATH << fileInfo.filePath << endl;
136 return false;
137 }
138
139 if (!cJSON_IsString(nameNode)) {
140 cerr << "Error: name must string." << NEW_LINE_PATH << fileInfo.filePath << endl;
141 return false;
142 }
143
144 if (isBaseString_ && !TranslatableParse::ParseTranslatable(objectNode, fileInfo, nameNode->valuestring)) {
145 return false;
146 }
147 ResourceItem resourceItem(nameNode->valuestring, fileInfo.keyParams, fileInfo.fileType);
148 resourceItem.SetFilePath(fileInfo.filePath);
149 resourceItem.SetLimitKey(fileInfo.limitKey);
150 auto ret = handles_.find(fileInfo.fileType);
151 if (ret == handles_.end()) {
152 cerr << "Error: json parser don't support " << ResourceUtil::ResTypeToString(fileInfo.fileType) << endl;
153 return false;
154 }
155
156 if (!ret->second(objectNode, resourceItem)) {
157 return false;
158 }
159
160 return MergeResourceItem(resourceItem);
161 }
162
HandleString(const cJSON * objectNode,ResourceItem & resourceItem) const163 bool JsonCompiler::HandleString(const cJSON *objectNode, ResourceItem &resourceItem) const
164 {
165 cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
166 if (!CheckJsonStringValue(valueNode, resourceItem)) {
167 return false;
168 }
169 return PushString(valueNode->valuestring, resourceItem);
170 }
171
HandleInteger(const cJSON * objectNode,ResourceItem & resourceItem) const172 bool JsonCompiler::HandleInteger(const cJSON *objectNode, ResourceItem &resourceItem) const
173 {
174 cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
175 if (!CheckJsonIntegerValue(valueNode, resourceItem)) {
176 return false;
177 }
178 if (cJSON_IsString(valueNode)) {
179 return PushString(valueNode->valuestring, resourceItem);
180 } else if (cJSON_IsNumber(valueNode)) {
181 return PushString(to_string(valueNode->valueint), resourceItem);
182 } else {
183 return false;
184 }
185 }
186
HandleBoolean(const cJSON * objectNode,ResourceItem & resourceItem) const187 bool JsonCompiler::HandleBoolean(const cJSON *objectNode, ResourceItem &resourceItem) const
188 {
189 cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
190 if (cJSON_IsString(valueNode)) {
191 regex ref("^\\$(ohos:)?boolean:.*");
192 if (!regex_match(valueNode->valuestring, ref)) {
193 cerr << "Error: '" << valueNode->valuestring << "' only refer '$boolean:xxx'.";
194 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
195 return false;
196 }
197 return PushString(valueNode->valuestring, resourceItem);
198 }
199 if (!cJSON_IsBool(valueNode)) {
200 cerr << "Error: '" << resourceItem.GetName() << "' value not boolean.";
201 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
202 return false;
203 }
204 return PushString(cJSON_IsTrue(valueNode) == 1 ? "true" : "false", resourceItem);
205 }
206
HandleColor(const cJSON * objectNode,ResourceItem & resourceItem) const207 bool JsonCompiler::HandleColor(const cJSON *objectNode, ResourceItem &resourceItem) const
208 {
209 return HandleString(objectNode, resourceItem);
210 }
211
HandleFloat(const cJSON * objectNode,ResourceItem & resourceItem) const212 bool JsonCompiler::HandleFloat(const cJSON *objectNode, ResourceItem &resourceItem) const
213 {
214 return HandleString(objectNode, resourceItem);
215 }
216
HandleStringArray(const cJSON * objectNode,ResourceItem & resourceItem) const217 bool JsonCompiler::HandleStringArray(const cJSON *objectNode, ResourceItem &resourceItem) const
218 {
219 vector<string> extra;
220 return ParseValueArray(objectNode, resourceItem, extra,
221 [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) -> bool {
222 if (!cJSON_IsObject(arrayItem)) {
223 cerr << "Error: '" << resourceItem.GetName() << "' value array item not object.";
224 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
225 return false;
226 }
227 cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
228 if (!CheckJsonStringValue(valueNode, resourceItem)) {
229 return false;
230 }
231 values.push_back(valueNode->valuestring);
232 return true;
233 });
234 }
235
HandleIntegerArray(const cJSON * objectNode,ResourceItem & resourceItem) const236 bool JsonCompiler::HandleIntegerArray(const cJSON *objectNode, ResourceItem &resourceItem) const
237 {
238 vector<string> extra;
239 return ParseValueArray(objectNode, resourceItem, extra,
240 [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) -> bool {
241 if (!CheckJsonIntegerValue(arrayItem, resourceItem)) {
242 return false;
243 }
244 if (cJSON_IsString(arrayItem)) {
245 values.push_back(arrayItem->valuestring);
246 } else {
247 values.push_back(to_string(arrayItem->valueint));
248 }
249 return true;
250 });
251 }
252
HandleTheme(const cJSON * objectNode,ResourceItem & resourceItem) const253 bool JsonCompiler::HandleTheme(const cJSON *objectNode, ResourceItem &resourceItem) const
254 {
255 vector<string> extra;
256 if (!ParseParent(objectNode, resourceItem, extra)) {
257 return false;
258 }
259 return ParseValueArray(objectNode, resourceItem, extra,
260 [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) {
261 return ParseAttribute(arrayItem, resourceItem, values);
262 });
263 }
264
HandlePattern(const cJSON * objectNode,ResourceItem & resourceItem) const265 bool JsonCompiler::HandlePattern(const cJSON *objectNode, ResourceItem &resourceItem) const
266 {
267 return HandleTheme(objectNode, resourceItem);
268 }
269
HandlePlural(const cJSON * objectNode,ResourceItem & resourceItem) const270 bool JsonCompiler::HandlePlural(const cJSON *objectNode, ResourceItem &resourceItem) const
271 {
272 vector<string> extra;
273 vector<string> attrs;
274 bool result = ParseValueArray(objectNode, resourceItem, extra,
275 [&attrs, this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) {
276 if (!CheckPluralValue(arrayItem, resourceItem)) {
277 return false;
278 }
279 cJSON *quantityNode = cJSON_GetObjectItem(arrayItem, TAG_QUANTITY.c_str());
280 if (!quantityNode || !cJSON_IsString(quantityNode)) {
281 return false;
282 }
283 string quantityValue = quantityNode->valuestring;
284 if (find(attrs.begin(), attrs.end(), quantityValue) != attrs.end()) {
285 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
286 cerr << "' duplicated." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
287 return false;
288 }
289 attrs.push_back(quantityValue);
290 values.push_back(quantityValue);
291 cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
292 if (!valueNode || !cJSON_IsString(valueNode)) {
293 return false;
294 }
295 values.push_back(valueNode->valuestring);
296 return true;
297 });
298 if (!result) {
299 return false;
300 }
301 if (find(attrs.begin(), attrs.end(), "other") == attrs.end()) {
302 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity must contains 'other'.";
303 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
304 return false;
305 }
306 return true;
307 }
308
HandleSymbol(const cJSON * objectNode,ResourceItem & resourceItem) const309 bool JsonCompiler::HandleSymbol(const cJSON *objectNode, ResourceItem &resourceItem) const
310 {
311 cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
312 if (!CheckJsonSymbolValue(valueNode, resourceItem)) {
313 return false;
314 }
315 return PushString(valueNode->valuestring, resourceItem);
316 }
317
PushString(const string & value,ResourceItem & resourceItem) const318 bool JsonCompiler::PushString(const string &value, ResourceItem &resourceItem) const
319 {
320 if (!resourceItem.SetData(reinterpret_cast<const int8_t *>(value.c_str()), value.length())) {
321 cerr << "Error: resourceItem setdata fail,'" << resourceItem.GetName() << "'.";
322 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
323 return false;
324 }
325 return true;
326 }
327
CheckJsonStringValue(const cJSON * valueNode,const ResourceItem & resourceItem) const328 bool JsonCompiler::CheckJsonStringValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
329 {
330 if (!valueNode || !cJSON_IsString(valueNode)) {
331 cerr << "Error: '" << resourceItem.GetName() << "' value not string.";
332 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
333 return false;
334 }
335
336 const map<ResType, string> REFS = {
337 { ResType::STRING, "\\$(ohos:)?string:" },
338 { ResType::STRARRAY, "\\$(ohos:)?string:" },
339 { ResType::COLOR, "\\$(ohos:)?color:" },
340 { ResType::FLOAT, "\\$(ohos:)?float:" }
341 };
342
343 string value = valueNode->valuestring;
344 ResType type = resourceItem.GetResType();
345 if (type == ResType::COLOR && !CheckColorValue(value.c_str())) {
346 string error = "invalid color value '" + value + \
347 "', only support refer '$color:xxx' or '#rgb','#argb','#rrggbb','#aarrggbb'.";
348 cerr << "Error: " << error << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
349 return false;
350 }
351 regex ref("^\\$.+:");
352 smatch result;
353 if (regex_search(value, result, ref) && !regex_match(result[0].str(), regex(REFS.at(type)))) {
354 cerr << "Error: '" << value << "', only refer '"<< REFS.at(type) << "xxx'.";
355 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
356 return false;
357 }
358 return true;
359 }
360
CheckJsonIntegerValue(const cJSON * valueNode,const ResourceItem & resourceItem) const361 bool JsonCompiler::CheckJsonIntegerValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
362 {
363 if (!valueNode) {
364 cerr << "Error: '" << resourceItem.GetName() << "' value is empty";
365 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
366 return false;
367 }
368 if (cJSON_IsString(valueNode)) {
369 regex ref("^\\$(ohos:)?integer:.*");
370 if (!regex_match(valueNode->valuestring, ref)) {
371 cerr << "Error: '" << valueNode->valuestring << "', only refer '$integer:xxx'.";
372 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
373 return false;
374 }
375 } else if (!ResourceUtil::IsIntValue(valueNode)) {
376 cerr << "Error: '" << resourceItem.GetName() << "' value not integer.";
377 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
378 return false;
379 }
380 return true;
381 }
382
CheckJsonSymbolValue(const cJSON * valueNode,const ResourceItem & resourceItem) const383 bool JsonCompiler::CheckJsonSymbolValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
384 {
385 if (!valueNode || !cJSON_IsString(valueNode)) {
386 cerr << "Error: '" << resourceItem.GetName() << "' value not string.";
387 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
388 return false;
389 }
390 string unicodeStr = valueNode->valuestring;
391 if (regex_match(unicodeStr, regex("^\\$(ohos:)?symbol:.*"))) {
392 return true;
393 }
394 int unicode = strtol(unicodeStr.c_str(), nullptr, 16);
395 if (!ResourceUtil::isUnicodeInPlane15or16(unicode)) {
396 cerr << "Error: '" << resourceItem.GetName() << "' value must in 0xF0000 ~ 0xFFFFF or 0x100000 ~ 0x10FFFF.";
397 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
398 return false;
399 }
400 return true;
401 }
402
ParseValueArray(const cJSON * objectNode,ResourceItem & resourceItem,const vector<string> & extra,HandleValue callback) const403 bool JsonCompiler::ParseValueArray(const cJSON *objectNode, ResourceItem &resourceItem,
404 const vector<string> &extra, HandleValue callback) const
405 {
406 cJSON *arrayNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
407 if (!cJSON_IsArray(arrayNode)) {
408 cerr << "Error: '" << resourceItem.GetName() << "' value not array.";
409 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
410 return false;
411 }
412
413 if (cJSON_GetArraySize(arrayNode) == 0) {
414 cerr << "Error: '" << resourceItem.GetName() << "' value empty.";
415 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
416 return false;
417 }
418
419 vector<string> contents;
420 if (!extra.empty()) {
421 contents.assign(extra.begin(), extra.end());
422 }
423 for (cJSON *item = arrayNode->child; item; item = item->next) {
424 vector<string> values;
425 if (!callback(item, resourceItem, values)) {
426 return false;
427 }
428 contents.insert(contents.end(), values.begin(), values.end());
429 }
430
431 string data = ResourceUtil::ComposeStrings(contents);
432 if (data.empty()) {
433 cerr << "Error: '" << resourceItem.GetName() << "' array too large.";
434 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
435 return false;
436 }
437 return PushString(data, resourceItem);
438 }
439
ParseParent(const cJSON * objectNode,const ResourceItem & resourceItem,vector<string> & extra) const440 bool JsonCompiler::ParseParent(const cJSON *objectNode, const ResourceItem &resourceItem,
441 vector<string> &extra) const
442 {
443 cJSON *parentNode = cJSON_GetObjectItem(objectNode, TAG_PARENT.c_str());
444 string type = ResourceUtil::ResTypeToString(resourceItem.GetResType());
445 if (parentNode) {
446 if (!cJSON_IsString(parentNode)) {
447 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' parent not string.";
448 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
449 return false;
450 }
451 string parentValue = parentNode->valuestring;
452 if (parentValue.empty()) {
453 cerr << "Error: " << type << " '"<< resourceItem.GetName() << "' parent empty.";
454 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
455 return false;
456 }
457 if (regex_match(parentValue, regex("^ohos:" + type + ":.+"))) {
458 parentValue = "$" + parentValue;
459 } else {
460 parentValue = "$" + type + ":" + parentValue;
461 }
462 extra.push_back(parentValue);
463 }
464 return true;
465 }
466
ParseAttribute(const cJSON * arrayItem,const ResourceItem & resourceItem,vector<string> & values) const467 bool JsonCompiler::ParseAttribute(const cJSON *arrayItem, const ResourceItem &resourceItem,
468 vector<string> &values) const
469 {
470 string type = ResourceUtil::ResTypeToString(resourceItem.GetResType());
471 if (!arrayItem || !cJSON_IsObject(arrayItem)) {
472 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute not object.";
473 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
474 return false;
475 }
476 cJSON *nameNode = cJSON_GetObjectItem(arrayItem, TAG_NAME.c_str());
477 if (!nameNode) {
478 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute name empty.";
479 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
480 return false;
481 }
482 if (!cJSON_IsString(nameNode)) {
483 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute name not string.";
484 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
485 return false;
486 }
487 values.push_back(nameNode->valuestring);
488
489 cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
490 if (!valueNode) {
491 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute '" << nameNode->valuestring;
492 cerr << "' value empty." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
493 return false;
494 }
495 if (!cJSON_IsString(valueNode)) {
496 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute '" << nameNode->valuestring;
497 cerr << "' value not string." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
498 return false;
499 }
500 values.push_back(valueNode->valuestring);
501 return true;
502 }
503
CheckPluralValue(const cJSON * arrayItem,const ResourceItem & resourceItem) const504 bool JsonCompiler::CheckPluralValue(const cJSON *arrayItem, const ResourceItem &resourceItem) const
505 {
506 if (!arrayItem || !cJSON_IsObject(arrayItem)) {
507 cerr << "Error: Plural '" << resourceItem.GetName() << "' array item not object.";
508 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
509 return false;
510 }
511 cJSON *quantityNode = cJSON_GetObjectItem(arrayItem, TAG_QUANTITY.c_str());
512 if (!quantityNode) {
513 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity empty.";
514 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
515 return false;
516 }
517 if (!cJSON_IsString(quantityNode)) {
518 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity not string.";
519 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
520 return false;
521 }
522 string quantityValue = quantityNode->valuestring;
523 if (find(QUANTITY_ATTRS.begin(), QUANTITY_ATTRS.end(), quantityValue) == QUANTITY_ATTRS.end()) {
524 string buffer(" ");
525 for_each(QUANTITY_ATTRS.begin(), QUANTITY_ATTRS.end(), [&buffer](auto iter) {
526 buffer.append(iter).append(" ");
527 });
528 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
529 cerr << "' not in [" << buffer << "]." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
530 return false;
531 }
532
533 cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
534 if (!valueNode) {
535 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
536 cerr << "' value empty." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
537 return false;
538 }
539 if (!cJSON_IsString(valueNode)) {
540 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
541 cerr << "' value not string." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
542 return false;
543 }
544 return true;
545 }
546
CheckColorValue(const char * s) const547 bool JsonCompiler::CheckColorValue(const char *s) const
548 {
549 if (s == nullptr) {
550 return false;
551 }
552 // color regex
553 string regColor = "^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$";
554 if (regex_match(s, regex("^\\$.*")) || regex_match(s, regex(regColor))) {
555 return true;
556 }
557 return false;
558 }
559 }
560 }
561 }
562