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 (valueNode == nullptr) {
191 cerr << "Error: '" << resourceItem.GetName() << "' value not json.";
192 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
193 return false;
194 }
195 if (cJSON_IsString(valueNode)) {
196 regex ref("^\\$(ohos:)?boolean:.*");
197 if (!regex_match(valueNode->valuestring, ref)) {
198 cerr << "Error: '" << valueNode->valuestring << "' only refer '$boolean:xxx'.";
199 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
200 return false;
201 }
202 return PushString(valueNode->valuestring, resourceItem);
203 }
204 if (!cJSON_IsBool(valueNode)) {
205 cerr << "Error: '" << resourceItem.GetName() << "' value not boolean.";
206 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
207 return false;
208 }
209 return PushString(cJSON_IsTrue(valueNode) == 1 ? "true" : "false", resourceItem);
210 }
211
HandleColor(const cJSON * objectNode,ResourceItem & resourceItem) const212 bool JsonCompiler::HandleColor(const cJSON *objectNode, ResourceItem &resourceItem) const
213 {
214 return HandleString(objectNode, resourceItem);
215 }
216
HandleFloat(const cJSON * objectNode,ResourceItem & resourceItem) const217 bool JsonCompiler::HandleFloat(const cJSON *objectNode, ResourceItem &resourceItem) const
218 {
219 return HandleString(objectNode, resourceItem);
220 }
221
HandleStringArray(const cJSON * objectNode,ResourceItem & resourceItem) const222 bool JsonCompiler::HandleStringArray(const cJSON *objectNode, ResourceItem &resourceItem) const
223 {
224 vector<string> extra;
225 return ParseValueArray(objectNode, resourceItem, extra,
226 [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) -> bool {
227 if (!cJSON_IsObject(arrayItem)) {
228 cerr << "Error: '" << resourceItem.GetName() << "' value array item not object.";
229 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
230 return false;
231 }
232 cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
233 if (!CheckJsonStringValue(valueNode, resourceItem)) {
234 return false;
235 }
236 values.push_back(valueNode->valuestring);
237 return true;
238 });
239 }
240
HandleIntegerArray(const cJSON * objectNode,ResourceItem & resourceItem) const241 bool JsonCompiler::HandleIntegerArray(const cJSON *objectNode, ResourceItem &resourceItem) const
242 {
243 vector<string> extra;
244 return ParseValueArray(objectNode, resourceItem, extra,
245 [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) -> bool {
246 if (!CheckJsonIntegerValue(arrayItem, resourceItem)) {
247 return false;
248 }
249 if (cJSON_IsString(arrayItem)) {
250 values.push_back(arrayItem->valuestring);
251 } else {
252 values.push_back(to_string(arrayItem->valueint));
253 }
254 return true;
255 });
256 }
257
HandleTheme(const cJSON * objectNode,ResourceItem & resourceItem) const258 bool JsonCompiler::HandleTheme(const cJSON *objectNode, ResourceItem &resourceItem) const
259 {
260 vector<string> extra;
261 if (!ParseParent(objectNode, resourceItem, extra)) {
262 return false;
263 }
264 return ParseValueArray(objectNode, resourceItem, extra,
265 [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) {
266 return ParseAttribute(arrayItem, resourceItem, values);
267 });
268 }
269
HandlePattern(const cJSON * objectNode,ResourceItem & resourceItem) const270 bool JsonCompiler::HandlePattern(const cJSON *objectNode, ResourceItem &resourceItem) const
271 {
272 return HandleTheme(objectNode, resourceItem);
273 }
274
HandlePlural(const cJSON * objectNode,ResourceItem & resourceItem) const275 bool JsonCompiler::HandlePlural(const cJSON *objectNode, ResourceItem &resourceItem) const
276 {
277 vector<string> extra;
278 vector<string> attrs;
279 bool result = ParseValueArray(objectNode, resourceItem, extra,
280 [&attrs, this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) {
281 if (!CheckPluralValue(arrayItem, resourceItem)) {
282 return false;
283 }
284 cJSON *quantityNode = cJSON_GetObjectItem(arrayItem, TAG_QUANTITY.c_str());
285 if (!quantityNode || !cJSON_IsString(quantityNode)) {
286 return false;
287 }
288 string quantityValue = quantityNode->valuestring;
289 if (find(attrs.begin(), attrs.end(), quantityValue) != attrs.end()) {
290 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
291 cerr << "' duplicated." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
292 return false;
293 }
294 attrs.push_back(quantityValue);
295 values.push_back(quantityValue);
296 cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
297 if (!valueNode || !cJSON_IsString(valueNode)) {
298 return false;
299 }
300 values.push_back(valueNode->valuestring);
301 return true;
302 });
303 if (!result) {
304 return false;
305 }
306 if (find(attrs.begin(), attrs.end(), "other") == attrs.end()) {
307 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity must contains 'other'.";
308 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
309 return false;
310 }
311 return true;
312 }
313
HandleSymbol(const cJSON * objectNode,ResourceItem & resourceItem) const314 bool JsonCompiler::HandleSymbol(const cJSON *objectNode, ResourceItem &resourceItem) const
315 {
316 cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
317 if (!CheckJsonSymbolValue(valueNode, resourceItem)) {
318 return false;
319 }
320 return PushString(valueNode->valuestring, resourceItem);
321 }
322
PushString(const string & value,ResourceItem & resourceItem) const323 bool JsonCompiler::PushString(const string &value, ResourceItem &resourceItem) const
324 {
325 if (!resourceItem.SetData(reinterpret_cast<const int8_t *>(value.c_str()), value.length())) {
326 cerr << "Error: resourceItem setdata fail,'" << resourceItem.GetName() << "'.";
327 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
328 return false;
329 }
330 return true;
331 }
332
CheckJsonStringValue(const cJSON * valueNode,const ResourceItem & resourceItem) const333 bool JsonCompiler::CheckJsonStringValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
334 {
335 if (!valueNode || !cJSON_IsString(valueNode)) {
336 cerr << "Error: '" << resourceItem.GetName() << "' value not string.";
337 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
338 return false;
339 }
340
341 const map<ResType, string> REFS = {
342 { ResType::STRING, "\\$(ohos:)?string:" },
343 { ResType::STRARRAY, "\\$(ohos:)?string:" },
344 { ResType::COLOR, "\\$(ohos:)?color:" },
345 { ResType::FLOAT, "\\$(ohos:)?float:" }
346 };
347
348 string value = valueNode->valuestring;
349 ResType type = resourceItem.GetResType();
350 if (type == ResType::COLOR && !CheckColorValue(value.c_str())) {
351 string error = "invalid color value '" + value + \
352 "', only support refer '$color:xxx' or '#rgb','#argb','#rrggbb','#aarrggbb'.";
353 cerr << "Error: " << error << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
354 return false;
355 }
356 regex ref("^\\$.+:");
357 smatch result;
358 if (regex_search(value, result, ref) && !regex_match(result[0].str(), regex(REFS.at(type)))) {
359 cerr << "Error: '" << value << "', only refer '"<< REFS.at(type) << "xxx'.";
360 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
361 return false;
362 }
363 return true;
364 }
365
CheckJsonIntegerValue(const cJSON * valueNode,const ResourceItem & resourceItem) const366 bool JsonCompiler::CheckJsonIntegerValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
367 {
368 if (!valueNode) {
369 cerr << "Error: '" << resourceItem.GetName() << "' value is empty";
370 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
371 return false;
372 }
373 if (cJSON_IsString(valueNode)) {
374 regex ref("^\\$(ohos:)?integer:.*");
375 if (!regex_match(valueNode->valuestring, ref)) {
376 cerr << "Error: '" << valueNode->valuestring << "', only refer '$integer:xxx'.";
377 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
378 return false;
379 }
380 } else if (!ResourceUtil::IsIntValue(valueNode)) {
381 cerr << "Error: '" << resourceItem.GetName() << "' value not integer.";
382 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
383 return false;
384 }
385 return true;
386 }
387
CheckJsonSymbolValue(const cJSON * valueNode,const ResourceItem & resourceItem) const388 bool JsonCompiler::CheckJsonSymbolValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
389 {
390 if (!valueNode || !cJSON_IsString(valueNode)) {
391 cerr << "Error: '" << resourceItem.GetName() << "' value not string.";
392 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
393 return false;
394 }
395 string unicodeStr = valueNode->valuestring;
396 if (regex_match(unicodeStr, regex("^\\$(ohos:)?symbol:.*"))) {
397 return true;
398 }
399 int unicode = strtol(unicodeStr.c_str(), nullptr, 16);
400 if (!ResourceUtil::isUnicodeInPlane15or16(unicode)) {
401 cerr << "Error: '" << resourceItem.GetName() << "' value must in 0xF0000 ~ 0xFFFFF or 0x100000 ~ 0x10FFFF.";
402 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
403 return false;
404 }
405 return true;
406 }
407
ParseValueArray(const cJSON * objectNode,ResourceItem & resourceItem,const vector<string> & extra,HandleValue callback) const408 bool JsonCompiler::ParseValueArray(const cJSON *objectNode, ResourceItem &resourceItem,
409 const vector<string> &extra, HandleValue callback) const
410 {
411 cJSON *arrayNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
412 if (arrayNode == nullptr) {
413 cerr << "Error: '" << resourceItem.GetName() << "' value not json.";
414 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
415 return false;
416 }
417
418 if (!cJSON_IsArray(arrayNode)) {
419 cerr << "Error: '" << resourceItem.GetName() << "' value not array.";
420 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
421 return false;
422 }
423
424 if (cJSON_GetArraySize(arrayNode) == 0) {
425 cerr << "Error: '" << resourceItem.GetName() << "' value empty.";
426 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
427 return false;
428 }
429
430 vector<string> contents;
431 if (!extra.empty()) {
432 contents.assign(extra.begin(), extra.end());
433 }
434 for (cJSON *item = arrayNode->child; item; item = item->next) {
435 vector<string> values;
436 if (!callback(item, resourceItem, values)) {
437 return false;
438 }
439 contents.insert(contents.end(), values.begin(), values.end());
440 }
441
442 string data = ResourceUtil::ComposeStrings(contents);
443 if (data.empty()) {
444 cerr << "Error: '" << resourceItem.GetName() << "' array too large.";
445 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
446 return false;
447 }
448 return PushString(data, resourceItem);
449 }
450
ParseParent(const cJSON * objectNode,const ResourceItem & resourceItem,vector<string> & extra) const451 bool JsonCompiler::ParseParent(const cJSON *objectNode, const ResourceItem &resourceItem,
452 vector<string> &extra) const
453 {
454 cJSON *parentNode = cJSON_GetObjectItem(objectNode, TAG_PARENT.c_str());
455 string type = ResourceUtil::ResTypeToString(resourceItem.GetResType());
456 if (parentNode) {
457 if (!cJSON_IsString(parentNode)) {
458 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' parent not string.";
459 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
460 return false;
461 }
462 string parentValue = parentNode->valuestring;
463 if (parentValue.empty()) {
464 cerr << "Error: " << type << " '"<< resourceItem.GetName() << "' parent empty.";
465 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
466 return false;
467 }
468 if (regex_match(parentValue, regex("^ohos:" + type + ":.+"))) {
469 parentValue = "$" + parentValue;
470 } else {
471 parentValue = "$" + type + ":" + parentValue;
472 }
473 extra.push_back(parentValue);
474 }
475 return true;
476 }
477
ParseAttribute(const cJSON * arrayItem,const ResourceItem & resourceItem,vector<string> & values) const478 bool JsonCompiler::ParseAttribute(const cJSON *arrayItem, const ResourceItem &resourceItem,
479 vector<string> &values) const
480 {
481 string type = ResourceUtil::ResTypeToString(resourceItem.GetResType());
482 if (!arrayItem || !cJSON_IsObject(arrayItem)) {
483 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute not object.";
484 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
485 return false;
486 }
487 cJSON *nameNode = cJSON_GetObjectItem(arrayItem, TAG_NAME.c_str());
488 if (!nameNode) {
489 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute name empty.";
490 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
491 return false;
492 }
493 if (!cJSON_IsString(nameNode)) {
494 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute name not string.";
495 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
496 return false;
497 }
498 values.push_back(nameNode->valuestring);
499
500 cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
501 if (!valueNode) {
502 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute '" << nameNode->valuestring;
503 cerr << "' value empty." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
504 return false;
505 }
506 if (!cJSON_IsString(valueNode)) {
507 cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute '" << nameNode->valuestring;
508 cerr << "' value not string." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
509 return false;
510 }
511 values.push_back(valueNode->valuestring);
512 return true;
513 }
514
CheckPluralValue(const cJSON * arrayItem,const ResourceItem & resourceItem) const515 bool JsonCompiler::CheckPluralValue(const cJSON *arrayItem, const ResourceItem &resourceItem) const
516 {
517 if (!arrayItem || !cJSON_IsObject(arrayItem)) {
518 cerr << "Error: Plural '" << resourceItem.GetName() << "' array item not object.";
519 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
520 return false;
521 }
522 cJSON *quantityNode = cJSON_GetObjectItem(arrayItem, TAG_QUANTITY.c_str());
523 if (!quantityNode) {
524 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity empty.";
525 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
526 return false;
527 }
528 if (!cJSON_IsString(quantityNode)) {
529 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity not string.";
530 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
531 return false;
532 }
533 string quantityValue = quantityNode->valuestring;
534 if (find(QUANTITY_ATTRS.begin(), QUANTITY_ATTRS.end(), quantityValue) == QUANTITY_ATTRS.end()) {
535 string buffer(" ");
536 for_each(QUANTITY_ATTRS.begin(), QUANTITY_ATTRS.end(), [&buffer](auto iter) {
537 buffer.append(iter).append(" ");
538 });
539 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
540 cerr << "' not in [" << buffer << "]." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
541 return false;
542 }
543
544 cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
545 if (!valueNode) {
546 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
547 cerr << "' value empty." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
548 return false;
549 }
550 if (!cJSON_IsString(valueNode)) {
551 cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
552 cerr << "' value not string." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
553 return false;
554 }
555 return true;
556 }
557
CheckColorValue(const char * s) const558 bool JsonCompiler::CheckColorValue(const char *s) const
559 {
560 if (s == nullptr) {
561 return false;
562 }
563 // color regex
564 string regColor = "^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$";
565 if (regex_match(s, regex("^\\$.*")) || regex_match(s, regex(regColor))) {
566 return true;
567 }
568 return false;
569 }
570 }
571 }
572 }
573