1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "Logger.h"
18 #include "ResourceParser.h"
19 #include "ResourceValues.h"
20 #include "ScopedXmlPullParser.h"
21 #include "SourceXmlPullParser.h"
22 #include "Util.h"
23 #include "XliffXmlPullParser.h"
24 
25 #include <sstream>
26 
27 namespace aapt {
28 
extractResourceName(const StringPiece16 & str,StringPiece16 * outPackage,StringPiece16 * outType,StringPiece16 * outEntry)29 void ResourceParser::extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
30                                          StringPiece16* outType, StringPiece16* outEntry) {
31     const char16_t* start = str.data();
32     const char16_t* end = start + str.size();
33     const char16_t* current = start;
34     while (current != end) {
35         if (outType->size() == 0 && *current == u'/') {
36             outType->assign(start, current - start);
37             start = current + 1;
38         } else if (outPackage->size() == 0 && *current == u':') {
39             outPackage->assign(start, current - start);
40             start = current + 1;
41         }
42         current++;
43     }
44     outEntry->assign(start, end - start);
45 }
46 
tryParseReference(const StringPiece16 & str,ResourceNameRef * outRef,bool * outCreate,bool * outPrivate)47 bool ResourceParser::tryParseReference(const StringPiece16& str, ResourceNameRef* outRef,
48                                        bool* outCreate, bool* outPrivate) {
49     StringPiece16 trimmedStr(util::trimWhitespace(str));
50     if (trimmedStr.empty()) {
51         return false;
52     }
53 
54     if (trimmedStr.data()[0] == u'@') {
55         size_t offset = 1;
56         *outCreate = false;
57         if (trimmedStr.data()[1] == u'+') {
58             *outCreate = true;
59             offset += 1;
60         } else if (trimmedStr.data()[1] == u'*') {
61             *outPrivate = true;
62             offset += 1;
63         }
64         StringPiece16 package;
65         StringPiece16 type;
66         StringPiece16 entry;
67         extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
68                             &package, &type, &entry);
69 
70         const ResourceType* parsedType = parseResourceType(type);
71         if (!parsedType) {
72             return false;
73         }
74 
75         if (*outCreate && *parsedType != ResourceType::kId) {
76             return false;
77         }
78 
79         outRef->package = package;
80         outRef->type = *parsedType;
81         outRef->entry = entry;
82         return true;
83     }
84     return false;
85 }
86 
tryParseAttributeReference(const StringPiece16 & str,ResourceNameRef * outRef)87 bool ResourceParser::tryParseAttributeReference(const StringPiece16& str,
88                                                 ResourceNameRef* outRef) {
89     StringPiece16 trimmedStr(util::trimWhitespace(str));
90     if (trimmedStr.empty()) {
91         return false;
92     }
93 
94     if (*trimmedStr.data() == u'?') {
95         StringPiece16 package;
96         StringPiece16 type;
97         StringPiece16 entry;
98         extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), &package, &type, &entry);
99 
100         if (!type.empty() && type != u"attr") {
101             return false;
102         }
103 
104         outRef->package = package;
105         outRef->type = ResourceType::kAttr;
106         outRef->entry = entry;
107         return true;
108     }
109     return false;
110 }
111 
112 /*
113  * Style parent's are a bit different. We accept the following formats:
114  *
115  * @[package:]style/<entry>
116  * ?[package:]style/<entry>
117  * <package>:[style/]<entry>
118  * [package:style/]<entry>
119  */
parseStyleParentReference(const StringPiece16 & str,Reference * outReference,std::string * outError)120 bool ResourceParser::parseStyleParentReference(const StringPiece16& str, Reference* outReference,
121                                                std::string* outError) {
122     if (str.empty()) {
123         return true;
124     }
125 
126     StringPiece16 name = str;
127 
128     bool hasLeadingIdentifiers = false;
129     bool privateRef = false;
130 
131     // Skip over these identifiers. A style's parent is a normal reference.
132     if (name.data()[0] == u'@' || name.data()[0] == u'?') {
133         hasLeadingIdentifiers = true;
134         name = name.substr(1, name.size() - 1);
135         if (name.data()[0] == u'*') {
136             privateRef = true;
137             name = name.substr(1, name.size() - 1);
138         }
139     }
140 
141     ResourceNameRef ref;
142     ref.type = ResourceType::kStyle;
143 
144     StringPiece16 typeStr;
145     extractResourceName(name, &ref.package, &typeStr, &ref.entry);
146     if (!typeStr.empty()) {
147         // If we have a type, make sure it is a Style.
148         const ResourceType* parsedType = parseResourceType(typeStr);
149         if (!parsedType || *parsedType != ResourceType::kStyle) {
150             std::stringstream err;
151             err << "invalid resource type '" << typeStr << "' for parent of style";
152             *outError = err.str();
153             return false;
154         }
155     } else {
156         // No type was defined, this should not have a leading identifier.
157         if (hasLeadingIdentifiers) {
158             std::stringstream err;
159             err << "invalid parent reference '" << str << "'";
160             *outError = err.str();
161             return false;
162         }
163     }
164 
165     if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
166         std::stringstream err;
167         err << "invalid parent reference '" << str << "'";
168         *outError = err.str();
169         return false;
170     }
171 
172     outReference->name = ref.toResourceName();
173     outReference->privateReference = privateRef;
174     return true;
175 }
176 
tryParseReference(const StringPiece16 & str,bool * outCreate)177 std::unique_ptr<Reference> ResourceParser::tryParseReference(const StringPiece16& str,
178                                                              bool* outCreate) {
179     ResourceNameRef ref;
180     bool privateRef = false;
181     if (tryParseReference(str, &ref, outCreate, &privateRef)) {
182         std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
183         value->privateReference = privateRef;
184         return value;
185     }
186 
187     if (tryParseAttributeReference(str, &ref)) {
188         *outCreate = false;
189         return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
190     }
191     return {};
192 }
193 
tryParseNullOrEmpty(const StringPiece16 & str)194 std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseNullOrEmpty(const StringPiece16& str) {
195     StringPiece16 trimmedStr(util::trimWhitespace(str));
196     android::Res_value value = {};
197     if (trimmedStr == u"@null") {
198         // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
199         // Instead we set the data type to TYPE_REFERENCE with a value of 0.
200         value.dataType = android::Res_value::TYPE_REFERENCE;
201     } else if (trimmedStr == u"@empty") {
202         // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
203         value.dataType = android::Res_value::TYPE_NULL;
204         value.data = android::Res_value::DATA_NULL_EMPTY;
205     } else {
206         return {};
207     }
208     return util::make_unique<BinaryPrimitive>(value);
209 }
210 
tryParseEnumSymbol(const Attribute & enumAttr,const StringPiece16 & str)211 std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseEnumSymbol(const Attribute& enumAttr,
212                                                                     const StringPiece16& str) {
213     StringPiece16 trimmedStr(util::trimWhitespace(str));
214     for (const auto& entry : enumAttr.symbols) {
215         // Enum symbols are stored as @package:id/symbol resources,
216         // so we need to match against the 'entry' part of the identifier.
217         const ResourceName& enumSymbolResourceName = entry.symbol.name;
218         if (trimmedStr == enumSymbolResourceName.entry) {
219             android::Res_value value = {};
220             value.dataType = android::Res_value::TYPE_INT_DEC;
221             value.data = entry.value;
222             return util::make_unique<BinaryPrimitive>(value);
223         }
224     }
225     return {};
226 }
227 
tryParseFlagSymbol(const Attribute & flagAttr,const StringPiece16 & str)228 std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseFlagSymbol(const Attribute& flagAttr,
229                                                                     const StringPiece16& str) {
230     android::Res_value flags = {};
231     flags.dataType = android::Res_value::TYPE_INT_DEC;
232 
233     for (StringPiece16 part : util::tokenize(str, u'|')) {
234         StringPiece16 trimmedPart = util::trimWhitespace(part);
235 
236         bool flagSet = false;
237         for (const auto& entry : flagAttr.symbols) {
238             // Flag symbols are stored as @package:id/symbol resources,
239             // so we need to match against the 'entry' part of the identifier.
240             const ResourceName& flagSymbolResourceName = entry.symbol.name;
241             if (trimmedPart == flagSymbolResourceName.entry) {
242                 flags.data |= entry.value;
243                 flagSet = true;
244                 break;
245             }
246         }
247 
248         if (!flagSet) {
249             return {};
250         }
251     }
252     return util::make_unique<BinaryPrimitive>(flags);
253 }
254 
parseHex(char16_t c,bool * outError)255 static uint32_t parseHex(char16_t c, bool* outError) {
256    if (c >= u'0' && c <= u'9') {
257         return c - u'0';
258     } else if (c >= u'a' && c <= u'f') {
259         return c - u'a' + 0xa;
260     } else if (c >= u'A' && c <= u'F') {
261         return c - u'A' + 0xa;
262     } else {
263         *outError = true;
264         return 0xffffffffu;
265     }
266 }
267 
tryParseColor(const StringPiece16 & str)268 std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseColor(const StringPiece16& str) {
269     StringPiece16 colorStr(util::trimWhitespace(str));
270     const char16_t* start = colorStr.data();
271     const size_t len = colorStr.size();
272     if (len == 0 || start[0] != u'#') {
273         return {};
274     }
275 
276     android::Res_value value = {};
277     bool error = false;
278     if (len == 4) {
279         value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
280         value.data = 0xff000000u;
281         value.data |= parseHex(start[1], &error) << 20;
282         value.data |= parseHex(start[1], &error) << 16;
283         value.data |= parseHex(start[2], &error) << 12;
284         value.data |= parseHex(start[2], &error) << 8;
285         value.data |= parseHex(start[3], &error) << 4;
286         value.data |= parseHex(start[3], &error);
287     } else if (len == 5) {
288         value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
289         value.data |= parseHex(start[1], &error) << 28;
290         value.data |= parseHex(start[1], &error) << 24;
291         value.data |= parseHex(start[2], &error) << 20;
292         value.data |= parseHex(start[2], &error) << 16;
293         value.data |= parseHex(start[3], &error) << 12;
294         value.data |= parseHex(start[3], &error) << 8;
295         value.data |= parseHex(start[4], &error) << 4;
296         value.data |= parseHex(start[4], &error);
297     } else if (len == 7) {
298         value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
299         value.data = 0xff000000u;
300         value.data |= parseHex(start[1], &error) << 20;
301         value.data |= parseHex(start[2], &error) << 16;
302         value.data |= parseHex(start[3], &error) << 12;
303         value.data |= parseHex(start[4], &error) << 8;
304         value.data |= parseHex(start[5], &error) << 4;
305         value.data |= parseHex(start[6], &error);
306     } else if (len == 9) {
307         value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
308         value.data |= parseHex(start[1], &error) << 28;
309         value.data |= parseHex(start[2], &error) << 24;
310         value.data |= parseHex(start[3], &error) << 20;
311         value.data |= parseHex(start[4], &error) << 16;
312         value.data |= parseHex(start[5], &error) << 12;
313         value.data |= parseHex(start[6], &error) << 8;
314         value.data |= parseHex(start[7], &error) << 4;
315         value.data |= parseHex(start[8], &error);
316     } else {
317         return {};
318     }
319     return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value);
320 }
321 
tryParseBool(const StringPiece16 & str)322 std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseBool(const StringPiece16& str) {
323     StringPiece16 trimmedStr(util::trimWhitespace(str));
324     uint32_t data = 0;
325     if (trimmedStr == u"true" || trimmedStr == u"TRUE") {
326         data = 0xffffffffu;
327     } else if (trimmedStr != u"false" && trimmedStr != u"FALSE") {
328         return {};
329     }
330     android::Res_value value = {};
331     value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
332     value.data = data;
333     return util::make_unique<BinaryPrimitive>(value);
334 }
335 
tryParseInt(const StringPiece16 & str)336 std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseInt(const StringPiece16& str) {
337     android::Res_value value;
338     if (!android::ResTable::stringToInt(str.data(), str.size(), &value)) {
339         return {};
340     }
341     return util::make_unique<BinaryPrimitive>(value);
342 }
343 
tryParseFloat(const StringPiece16 & str)344 std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseFloat(const StringPiece16& str) {
345     android::Res_value value;
346     if (!android::ResTable::stringToFloat(str.data(), str.size(), &value)) {
347         return {};
348     }
349     return util::make_unique<BinaryPrimitive>(value);
350 }
351 
androidTypeToAttributeTypeMask(uint16_t type)352 uint32_t ResourceParser::androidTypeToAttributeTypeMask(uint16_t type) {
353     switch (type) {
354         case android::Res_value::TYPE_NULL:
355         case android::Res_value::TYPE_REFERENCE:
356         case android::Res_value::TYPE_ATTRIBUTE:
357         case android::Res_value::TYPE_DYNAMIC_REFERENCE:
358             return android::ResTable_map::TYPE_REFERENCE;
359 
360         case android::Res_value::TYPE_STRING:
361             return android::ResTable_map::TYPE_STRING;
362 
363         case android::Res_value::TYPE_FLOAT:
364             return android::ResTable_map::TYPE_FLOAT;
365 
366         case android::Res_value::TYPE_DIMENSION:
367             return android::ResTable_map::TYPE_DIMENSION;
368 
369         case android::Res_value::TYPE_FRACTION:
370             return android::ResTable_map::TYPE_FRACTION;
371 
372         case android::Res_value::TYPE_INT_DEC:
373         case android::Res_value::TYPE_INT_HEX:
374             return android::ResTable_map::TYPE_INTEGER |
375                     android::ResTable_map::TYPE_ENUM |
376                     android::ResTable_map::TYPE_FLAGS;
377 
378         case android::Res_value::TYPE_INT_BOOLEAN:
379             return android::ResTable_map::TYPE_BOOLEAN;
380 
381         case android::Res_value::TYPE_INT_COLOR_ARGB8:
382         case android::Res_value::TYPE_INT_COLOR_RGB8:
383         case android::Res_value::TYPE_INT_COLOR_ARGB4:
384         case android::Res_value::TYPE_INT_COLOR_RGB4:
385             return android::ResTable_map::TYPE_COLOR;
386 
387         default:
388             return 0;
389     };
390 }
391 
parseItemForAttribute(const StringPiece16 & value,uint32_t typeMask,std::function<void (const ResourceName &)> onCreateReference)392 std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
393         const StringPiece16& value, uint32_t typeMask,
394         std::function<void(const ResourceName&)> onCreateReference) {
395     std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
396     if (nullOrEmpty) {
397         return std::move(nullOrEmpty);
398     }
399 
400     bool create = false;
401     std::unique_ptr<Reference> reference = tryParseReference(value, &create);
402     if (reference) {
403         if (create && onCreateReference) {
404             onCreateReference(reference->name);
405         }
406         return std::move(reference);
407     }
408 
409     if (typeMask & android::ResTable_map::TYPE_COLOR) {
410         // Try parsing this as a color.
411         std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
412         if (color) {
413             return std::move(color);
414         }
415     }
416 
417     if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
418         // Try parsing this as a boolean.
419         std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
420         if (boolean) {
421             return std::move(boolean);
422         }
423     }
424 
425     if (typeMask & android::ResTable_map::TYPE_INTEGER) {
426         // Try parsing this as an integer.
427         std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
428         if (integer) {
429             return std::move(integer);
430         }
431     }
432 
433     const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT |
434             android::ResTable_map::TYPE_DIMENSION |
435             android::ResTable_map::TYPE_FRACTION;
436     if (typeMask & floatMask) {
437         // Try parsing this as a float.
438         std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
439         if (floatingPoint) {
440             if (typeMask & androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
441                 return std::move(floatingPoint);
442             }
443         }
444     }
445     return {};
446 }
447 
448 /**
449  * We successively try to parse the string as a resource type that the Attribute
450  * allows.
451  */
parseItemForAttribute(const StringPiece16 & str,const Attribute & attr,std::function<void (const ResourceName &)> onCreateReference)452 std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
453         const StringPiece16& str, const Attribute& attr,
454         std::function<void(const ResourceName&)> onCreateReference) {
455     const uint32_t typeMask = attr.typeMask;
456     std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference);
457     if (value) {
458         return value;
459     }
460 
461     if (typeMask & android::ResTable_map::TYPE_ENUM) {
462         // Try parsing this as an enum.
463         std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
464         if (enumValue) {
465             return std::move(enumValue);
466         }
467     }
468 
469     if (typeMask & android::ResTable_map::TYPE_FLAGS) {
470         // Try parsing this as a flag.
471         std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
472         if (flagValue) {
473             return std::move(flagValue);
474         }
475     }
476     return {};
477 }
478 
ResourceParser(const std::shared_ptr<ResourceTable> & table,const Source & source,const ConfigDescription & config,const std::shared_ptr<XmlPullParser> & parser)479 ResourceParser::ResourceParser(const std::shared_ptr<ResourceTable>& table, const Source& source,
480                                const ConfigDescription& config,
481                                const std::shared_ptr<XmlPullParser>& parser) :
482         mTable(table), mSource(source), mConfig(config), mLogger(source),
483         mParser(std::make_shared<XliffXmlPullParser>(parser)) {
484 }
485 
486 /**
487  * Build a string from XML that converts nested elements into Span objects.
488  */
flattenXmlSubtree(XmlPullParser * parser,std::u16string * outRawString,StyleString * outStyleString)489 bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString,
490                                        StyleString* outStyleString) {
491     std::vector<Span> spanStack;
492 
493     outRawString->clear();
494     outStyleString->spans.clear();
495     util::StringBuilder builder;
496     size_t depth = 1;
497     while (XmlPullParser::isGoodEvent(parser->next())) {
498         const XmlPullParser::Event event = parser->getEvent();
499         if (event == XmlPullParser::Event::kEndElement) {
500             depth--;
501             if (depth == 0) {
502                 break;
503             }
504 
505             spanStack.back().lastChar = builder.str().size();
506             outStyleString->spans.push_back(spanStack.back());
507             spanStack.pop_back();
508 
509         } else if (event == XmlPullParser::Event::kText) {
510             // TODO(adamlesinski): Verify format strings.
511             outRawString->append(parser->getText());
512             builder.append(parser->getText());
513 
514         } else if (event == XmlPullParser::Event::kStartElement) {
515             if (parser->getElementNamespace().size() > 0) {
516                 mLogger.warn(parser->getLineNumber())
517                         << "skipping element '"
518                         << parser->getElementName()
519                         << "' with unknown namespace '"
520                         << parser->getElementNamespace()
521                         << "'."
522                         << std::endl;
523                 XmlPullParser::skipCurrentElement(parser);
524                 continue;
525             }
526             depth++;
527 
528             // Build a span object out of the nested element.
529             std::u16string spanName = parser->getElementName();
530             const auto endAttrIter = parser->endAttributes();
531             for (auto attrIter = parser->beginAttributes(); attrIter != endAttrIter; ++attrIter) {
532                 spanName += u";";
533                 spanName += attrIter->name;
534                 spanName += u"=";
535                 spanName += attrIter->value;
536             }
537 
538             if (builder.str().size() > std::numeric_limits<uint32_t>::max()) {
539                 mLogger.error(parser->getLineNumber())
540                         << "style string '"
541                         << builder.str()
542                         << "' is too long."
543                         << std::endl;
544                 return false;
545             }
546             spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.str().size()) });
547 
548         } else if (event == XmlPullParser::Event::kComment) {
549             // Skip
550         } else {
551             mLogger.warn(parser->getLineNumber())
552                     << "unknown event "
553                     << event
554                     << "."
555                     << std::endl;
556         }
557     }
558     assert(spanStack.empty() && "spans haven't been fully processed");
559 
560     outStyleString->str = builder.str();
561     return true;
562 }
563 
parse()564 bool ResourceParser::parse() {
565     while (XmlPullParser::isGoodEvent(mParser->next())) {
566         if (mParser->getEvent() != XmlPullParser::Event::kStartElement) {
567             continue;
568         }
569 
570         ScopedXmlPullParser parser(mParser.get());
571         if (!parser.getElementNamespace().empty() ||
572                 parser.getElementName() != u"resources") {
573             mLogger.error(parser.getLineNumber())
574                     << "root element must be <resources> in the global namespace."
575                     << std::endl;
576             return false;
577         }
578 
579         if (!parseResources(&parser)) {
580             return false;
581         }
582     }
583 
584     if (mParser->getEvent() == XmlPullParser::Event::kBadDocument) {
585         mLogger.error(mParser->getLineNumber())
586                 << mParser->getLastError()
587                 << std::endl;
588         return false;
589     }
590     return true;
591 }
592 
parseResources(XmlPullParser * parser)593 bool ResourceParser::parseResources(XmlPullParser* parser) {
594     bool success = true;
595 
596     std::u16string comment;
597     while (XmlPullParser::isGoodEvent(parser->next())) {
598         const XmlPullParser::Event event = parser->getEvent();
599         if (event == XmlPullParser::Event::kComment) {
600             comment = parser->getComment();
601             continue;
602         }
603 
604         if (event == XmlPullParser::Event::kText) {
605             if (!util::trimWhitespace(parser->getText()).empty()) {
606                 comment = u"";
607             }
608             continue;
609         }
610 
611         if (event != XmlPullParser::Event::kStartElement) {
612             continue;
613         }
614 
615         ScopedXmlPullParser childParser(parser);
616 
617         if (!childParser.getElementNamespace().empty()) {
618             // Skip unknown namespace.
619             continue;
620         }
621 
622         StringPiece16 name = childParser.getElementName();
623         if (name == u"skip" || name == u"eat-comment") {
624             continue;
625         }
626 
627         if (name == u"private-symbols") {
628             // Handle differently.
629             mLogger.note(childParser.getLineNumber())
630                     << "got a <private-symbols> tag."
631                     << std::endl;
632             continue;
633         }
634 
635         const auto endAttrIter = childParser.endAttributes();
636         auto attrIter = childParser.findAttribute(u"", u"name");
637         if (attrIter == endAttrIter || attrIter->value.empty()) {
638             mLogger.error(childParser.getLineNumber())
639                     << "<" << name << "> tag must have a 'name' attribute."
640                     << std::endl;
641             success = false;
642             continue;
643         }
644 
645         // Copy because our iterator will go out of scope when
646         // we parse more XML.
647         std::u16string attributeName = attrIter->value;
648 
649         if (name == u"item") {
650             // Items simply have their type encoded in the type attribute.
651             auto typeIter = childParser.findAttribute(u"", u"type");
652             if (typeIter == endAttrIter || typeIter->value.empty()) {
653                 mLogger.error(childParser.getLineNumber())
654                         << "<item> must have a 'type' attribute."
655                         << std::endl;
656                 success = false;
657                 continue;
658             }
659             name = typeIter->value;
660         }
661 
662         if (name == u"id") {
663             success &= mTable->addResource(ResourceNameRef{ {}, ResourceType::kId, attributeName },
664                                            {}, mSource.line(childParser.getLineNumber()),
665                                            util::make_unique<Id>());
666         } else if (name == u"string") {
667             success &= parseString(&childParser,
668                                    ResourceNameRef{ {}, ResourceType::kString, attributeName });
669         } else if (name == u"color") {
670             success &= parseColor(&childParser,
671                                   ResourceNameRef{ {}, ResourceType::kColor, attributeName });
672         } else if (name == u"drawable") {
673             success &= parseColor(&childParser,
674                                   ResourceNameRef{ {}, ResourceType::kDrawable, attributeName });
675         } else if (name == u"bool") {
676             success &= parsePrimitive(&childParser,
677                                       ResourceNameRef{ {}, ResourceType::kBool, attributeName });
678         } else if (name == u"integer") {
679             success &= parsePrimitive(
680                     &childParser,
681                     ResourceNameRef{ {}, ResourceType::kInteger, attributeName });
682         } else if (name == u"dimen") {
683             success &= parsePrimitive(&childParser,
684                                       ResourceNameRef{ {}, ResourceType::kDimen, attributeName });
685         } else if (name == u"fraction") {
686 //          success &= parsePrimitive(
687 //                  &childParser,
688 //                  ResourceNameRef{ {}, ResourceType::kFraction, attributeName });
689         } else if (name == u"style") {
690             success &= parseStyle(&childParser,
691                                   ResourceNameRef{ {}, ResourceType::kStyle, attributeName });
692         } else if (name == u"plurals") {
693             success &= parsePlural(&childParser,
694                                    ResourceNameRef{ {}, ResourceType::kPlurals, attributeName });
695         } else if (name == u"array") {
696             success &= parseArray(&childParser,
697                                   ResourceNameRef{ {}, ResourceType::kArray, attributeName },
698                                   android::ResTable_map::TYPE_ANY);
699         } else if (name == u"string-array") {
700             success &= parseArray(&childParser,
701                                   ResourceNameRef{ {}, ResourceType::kArray, attributeName },
702                                   android::ResTable_map::TYPE_STRING);
703         } else if (name == u"integer-array") {
704             success &= parseArray(&childParser,
705                                   ResourceNameRef{ {}, ResourceType::kArray, attributeName },
706                                   android::ResTable_map::TYPE_INTEGER);
707         } else if (name == u"public") {
708             success &= parsePublic(&childParser, attributeName);
709         } else if (name == u"declare-styleable") {
710             success &= parseDeclareStyleable(
711                     &childParser,
712                     ResourceNameRef{ {}, ResourceType::kStyleable, attributeName });
713         } else if (name == u"attr") {
714             success &= parseAttr(&childParser,
715                                  ResourceNameRef{ {}, ResourceType::kAttr, attributeName });
716         } else if (name == u"bag") {
717         } else if (name == u"public-padding") {
718         } else if (name == u"java-symbol") {
719         } else if (name == u"add-resource") {
720        }
721     }
722 
723     if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
724         mLogger.error(parser->getLineNumber())
725                 << parser->getLastError()
726                 << std::endl;
727         return false;
728     }
729     return success;
730 }
731 
732 
733 
734 enum {
735     kAllowRawString = true,
736     kNoRawString = false
737 };
738 
739 /**
740  * Reads the entire XML subtree and attempts to parse it as some Item,
741  * with typeMask denoting which items it can be. If allowRawValue is
742  * true, a RawString is returned if the XML couldn't be parsed as
743  * an Item. If allowRawValue is false, nullptr is returned in this
744  * case.
745  */
parseXml(XmlPullParser * parser,uint32_t typeMask,bool allowRawValue)746 std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, uint32_t typeMask,
747                                                bool allowRawValue) {
748     const size_t beginXmlLine = parser->getLineNumber();
749 
750     std::u16string rawValue;
751     StyleString styleString;
752     if (!flattenXmlSubtree(parser, &rawValue, &styleString)) {
753         return {};
754     }
755 
756     StringPool& pool = mTable->getValueStringPool();
757 
758     if (!styleString.spans.empty()) {
759         // This can only be a StyledString.
760         return util::make_unique<StyledString>(
761                 pool.makeRef(styleString, StringPool::Context{ 1, mConfig }));
762     }
763 
764     auto onCreateReference = [&](const ResourceName& name) {
765         // name.package can be empty here, as it will assume the package name of the table.
766         mTable->addResource(name, {}, mSource.line(beginXmlLine), util::make_unique<Id>());
767     };
768 
769     // Process the raw value.
770     std::unique_ptr<Item> processedItem = parseItemForAttribute(rawValue, typeMask,
771                                                                 onCreateReference);
772     if (processedItem) {
773         // Fix up the reference.
774         visitFunc<Reference>(*processedItem, [&](Reference& ref) {
775             if (!ref.name.package.empty()) {
776                 // The package name was set, so lookup its alias.
777                 parser->applyPackageAlias(&ref.name.package, mTable->getPackage());
778             } else {
779                 // The package name was left empty, so it assumes the default package
780                 // without alias lookup.
781                 ref.name.package = mTable->getPackage();
782             }
783         });
784         return processedItem;
785     }
786 
787     // Try making a regular string.
788     if (typeMask & android::ResTable_map::TYPE_STRING) {
789         // Use the trimmed, escaped string.
790         return util::make_unique<String>(
791                 pool.makeRef(styleString.str, StringPool::Context{ 1, mConfig }));
792     }
793 
794     // We can't parse this so return a RawString if we are allowed.
795     if (allowRawValue) {
796         return util::make_unique<RawString>(
797                 pool.makeRef(rawValue, StringPool::Context{ 1, mConfig }));
798     }
799     return {};
800 }
801 
parseString(XmlPullParser * parser,const ResourceNameRef & resourceName)802 bool ResourceParser::parseString(XmlPullParser* parser, const ResourceNameRef& resourceName) {
803     const SourceLine source = mSource.line(parser->getLineNumber());
804 
805     // Mark the string as untranslateable if needed.
806     const auto endAttrIter = parser->endAttributes();
807     auto attrIter = parser->findAttribute(u"", u"untranslateable");
808     // bool untranslateable = attrIter != endAttrIter;
809     // TODO(adamlesinski): Do something with this (mark the string).
810 
811     // Deal with the product.
812     attrIter = parser->findAttribute(u"", u"product");
813     if (attrIter != endAttrIter) {
814         if (attrIter->value != u"default" && attrIter->value != u"phone") {
815             // TODO(adamlesinski): Match products.
816             return true;
817         }
818     }
819 
820     std::unique_ptr<Item> processedItem = parseXml(parser, android::ResTable_map::TYPE_STRING,
821                                                    kNoRawString);
822     if (!processedItem) {
823         mLogger.error(source.line)
824                 << "not a valid string."
825                 << std::endl;
826         return false;
827     }
828 
829     return mTable->addResource(resourceName, mConfig, source, std::move(processedItem));
830 }
831 
parseColor(XmlPullParser * parser,const ResourceNameRef & resourceName)832 bool ResourceParser::parseColor(XmlPullParser* parser, const ResourceNameRef& resourceName) {
833     const SourceLine source = mSource.line(parser->getLineNumber());
834 
835     std::unique_ptr<Item> item = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString);
836     if (!item) {
837         mLogger.error(source.line) << "invalid color." << std::endl;
838         return false;
839     }
840     return mTable->addResource(resourceName, mConfig, source, std::move(item));
841 }
842 
parsePrimitive(XmlPullParser * parser,const ResourceNameRef & resourceName)843 bool ResourceParser::parsePrimitive(XmlPullParser* parser, const ResourceNameRef& resourceName) {
844     const SourceLine source = mSource.line(parser->getLineNumber());
845 
846     uint32_t typeMask = 0;
847     switch (resourceName.type) {
848         case ResourceType::kInteger:
849             typeMask |= android::ResTable_map::TYPE_INTEGER;
850             break;
851 
852         case ResourceType::kDimen:
853             typeMask |= android::ResTable_map::TYPE_DIMENSION
854                      | android::ResTable_map::TYPE_FLOAT
855                      | android::ResTable_map::TYPE_FRACTION;
856             break;
857 
858         case ResourceType::kBool:
859             typeMask |= android::ResTable_map::TYPE_BOOLEAN;
860             break;
861 
862         default:
863             assert(false);
864             break;
865     }
866 
867     std::unique_ptr<Item> item = parseXml(parser, typeMask, kNoRawString);
868     if (!item) {
869         mLogger.error(source.line)
870                 << "invalid "
871                 << resourceName.type
872                 << "."
873                 << std::endl;
874         return false;
875     }
876 
877     return mTable->addResource(resourceName, mConfig, source, std::move(item));
878 }
879 
parsePublic(XmlPullParser * parser,const StringPiece16 & name)880 bool ResourceParser::parsePublic(XmlPullParser* parser, const StringPiece16& name) {
881     const SourceLine source = mSource.line(parser->getLineNumber());
882 
883     const auto endAttrIter = parser->endAttributes();
884     const auto typeAttrIter = parser->findAttribute(u"", u"type");
885     if (typeAttrIter == endAttrIter || typeAttrIter->value.empty()) {
886         mLogger.error(source.line)
887                 << "<public> must have a 'type' attribute."
888                 << std::endl;
889         return false;
890     }
891 
892     const ResourceType* parsedType = parseResourceType(typeAttrIter->value);
893     if (!parsedType) {
894         mLogger.error(source.line)
895                 << "invalid resource type '"
896                 << typeAttrIter->value
897                 << "' in <public>."
898                 << std::endl;
899         return false;
900     }
901 
902     ResourceNameRef resourceName { {}, *parsedType, name };
903     ResourceId resourceId;
904 
905     const auto idAttrIter = parser->findAttribute(u"", u"id");
906     if (idAttrIter != endAttrIter && !idAttrIter->value.empty()) {
907         android::Res_value val;
908         bool result = android::ResTable::stringToInt(idAttrIter->value.data(),
909                                                      idAttrIter->value.size(), &val);
910         resourceId.id = val.data;
911         if (!result || !resourceId.isValid()) {
912             mLogger.error(source.line)
913                     << "invalid resource ID '"
914                     << idAttrIter->value
915                     << "' in <public>."
916                     << std::endl;
917             return false;
918         }
919     }
920 
921     if (*parsedType == ResourceType::kId) {
922         // An ID marked as public is also the definition of an ID.
923         mTable->addResource(resourceName, {}, source, util::make_unique<Id>());
924     }
925 
926     return mTable->markPublic(resourceName, resourceId, source);
927 }
928 
parseFormatType(const StringPiece16 & piece)929 static uint32_t parseFormatType(const StringPiece16& piece) {
930     if (piece == u"reference")      return android::ResTable_map::TYPE_REFERENCE;
931     else if (piece == u"string")    return android::ResTable_map::TYPE_STRING;
932     else if (piece == u"integer")   return android::ResTable_map::TYPE_INTEGER;
933     else if (piece == u"boolean")   return android::ResTable_map::TYPE_BOOLEAN;
934     else if (piece == u"color")     return android::ResTable_map::TYPE_COLOR;
935     else if (piece == u"float")     return android::ResTable_map::TYPE_FLOAT;
936     else if (piece == u"dimension") return android::ResTable_map::TYPE_DIMENSION;
937     else if (piece == u"fraction")  return android::ResTable_map::TYPE_FRACTION;
938     else if (piece == u"enum")      return android::ResTable_map::TYPE_ENUM;
939     else if (piece == u"flags")     return android::ResTable_map::TYPE_FLAGS;
940     return 0;
941 }
942 
parseFormatAttribute(const StringPiece16 & str)943 static uint32_t parseFormatAttribute(const StringPiece16& str) {
944     uint32_t mask = 0;
945     for (StringPiece16 part : util::tokenize(str, u'|')) {
946         StringPiece16 trimmedPart = util::trimWhitespace(part);
947         uint32_t type = parseFormatType(trimmedPart);
948         if (type == 0) {
949             return 0;
950         }
951         mask |= type;
952     }
953     return mask;
954 }
955 
parseAttr(XmlPullParser * parser,const ResourceNameRef & resourceName)956 bool ResourceParser::parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName) {
957     const SourceLine source = mSource.line(parser->getLineNumber());
958     ResourceName actualName = resourceName.toResourceName();
959     std::unique_ptr<Attribute> attr = parseAttrImpl(parser, &actualName, false);
960     if (!attr) {
961         return false;
962     }
963     return mTable->addResource(actualName, mConfig, source, std::move(attr));
964 }
965 
parseAttrImpl(XmlPullParser * parser,ResourceName * resourceName,bool weak)966 std::unique_ptr<Attribute> ResourceParser::parseAttrImpl(XmlPullParser* parser,
967                                                          ResourceName* resourceName,
968                                                          bool weak) {
969     uint32_t typeMask = 0;
970 
971     const auto endAttrIter = parser->endAttributes();
972     const auto formatAttrIter = parser->findAttribute(u"", u"format");
973     if (formatAttrIter != endAttrIter) {
974         typeMask = parseFormatAttribute(formatAttrIter->value);
975         if (typeMask == 0) {
976             mLogger.error(parser->getLineNumber())
977                     << "invalid attribute format '"
978                     << formatAttrIter->value
979                     << "'."
980                     << std::endl;
981             return {};
982         }
983     }
984 
985     // If this is a declaration, the package name may be in the name. Separate these out.
986     // Eg. <attr name="android:text" />
987     // No format attribute is allowed.
988     if (weak && formatAttrIter == endAttrIter) {
989         StringPiece16 package, type, name;
990         extractResourceName(resourceName->entry, &package, &type, &name);
991         if (type.empty() && !package.empty()) {
992             resourceName->package = package.toString();
993             resourceName->entry = name.toString();
994         }
995     }
996 
997     std::vector<Attribute::Symbol> items;
998 
999     bool error = false;
1000     while (XmlPullParser::isGoodEvent(parser->next())) {
1001         if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
1002             continue;
1003         }
1004 
1005         ScopedXmlPullParser childParser(parser);
1006 
1007         const std::u16string& name = childParser.getElementName();
1008         if (!childParser.getElementNamespace().empty()
1009                 || (name != u"flag" && name != u"enum")) {
1010             mLogger.error(childParser.getLineNumber())
1011                     << "unexpected tag <"
1012                     << name
1013                     << "> in <attr>."
1014                     << std::endl;
1015             error = true;
1016             continue;
1017         }
1018 
1019         if (name == u"enum") {
1020             if (typeMask & android::ResTable_map::TYPE_FLAGS) {
1021                 mLogger.error(childParser.getLineNumber())
1022                         << "can not define an <enum>; already defined a <flag>."
1023                         << std::endl;
1024                 error = true;
1025                 continue;
1026             }
1027             typeMask |= android::ResTable_map::TYPE_ENUM;
1028         } else if (name == u"flag") {
1029             if (typeMask & android::ResTable_map::TYPE_ENUM) {
1030                 mLogger.error(childParser.getLineNumber())
1031                         << "can not define a <flag>; already defined an <enum>."
1032                         << std::endl;
1033                 error = true;
1034                 continue;
1035             }
1036             typeMask |= android::ResTable_map::TYPE_FLAGS;
1037         }
1038 
1039         Attribute::Symbol item;
1040         if (parseEnumOrFlagItem(&childParser, name, &item)) {
1041             if (!mTable->addResource(item.symbol.name, mConfig,
1042                                      mSource.line(childParser.getLineNumber()),
1043                                      util::make_unique<Id>())) {
1044                 error = true;
1045             } else {
1046                 items.push_back(std::move(item));
1047             }
1048         } else {
1049             error = true;
1050         }
1051     }
1052 
1053     if (error) {
1054         return {};
1055     }
1056 
1057     std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
1058     attr->symbols.swap(items);
1059     attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY);
1060     return attr;
1061 }
1062 
parseEnumOrFlagItem(XmlPullParser * parser,const StringPiece16 & tag,Attribute::Symbol * outSymbol)1063 bool ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag,
1064                                          Attribute::Symbol* outSymbol) {
1065     const auto attrIterEnd = parser->endAttributes();
1066     const auto nameAttrIter = parser->findAttribute(u"", u"name");
1067     if (nameAttrIter == attrIterEnd || nameAttrIter->value.empty()) {
1068         mLogger.error(parser->getLineNumber())
1069                 << "no attribute 'name' found for tag <" << tag << ">."
1070                 << std::endl;
1071         return false;
1072     }
1073 
1074     const auto valueAttrIter = parser->findAttribute(u"", u"value");
1075     if (valueAttrIter == attrIterEnd || valueAttrIter->value.empty()) {
1076         mLogger.error(parser->getLineNumber())
1077                 << "no attribute 'value' found for tag <" << tag << ">."
1078                 << std::endl;
1079         return false;
1080     }
1081 
1082     android::Res_value val;
1083     if (!android::ResTable::stringToInt(valueAttrIter->value.data(),
1084                                         valueAttrIter->value.size(), &val)) {
1085         mLogger.error(parser->getLineNumber())
1086                 << "invalid value '"
1087                 << valueAttrIter->value
1088                 << "' for <" << tag << ">; must be an integer."
1089                 << std::endl;
1090         return false;
1091     }
1092 
1093     outSymbol->symbol.name = ResourceName {
1094             mTable->getPackage(), ResourceType::kId, nameAttrIter->value };
1095     outSymbol->value = val.data;
1096     return true;
1097 }
1098 
parseXmlAttributeName(StringPiece16 str,ResourceName * outName)1099 static bool parseXmlAttributeName(StringPiece16 str, ResourceName* outName) {
1100     str = util::trimWhitespace(str);
1101     const char16_t* const start = str.data();
1102     const char16_t* const end = start + str.size();
1103     const char16_t* p = start;
1104 
1105     StringPiece16 package;
1106     StringPiece16 name;
1107     while (p != end) {
1108         if (*p == u':') {
1109             package = StringPiece16(start, p - start);
1110             name = StringPiece16(p + 1, end - (p + 1));
1111             break;
1112         }
1113         p++;
1114     }
1115 
1116     outName->package = package.toString();
1117     outName->type = ResourceType::kAttr;
1118     if (name.size() == 0) {
1119         outName->entry = str.toString();
1120     } else {
1121         outName->entry = name.toString();
1122     }
1123     return true;
1124 }
1125 
parseUntypedItem(XmlPullParser * parser,Style & style)1126 bool ResourceParser::parseUntypedItem(XmlPullParser* parser, Style& style) {
1127     const auto endAttrIter = parser->endAttributes();
1128     const auto nameAttrIter = parser->findAttribute(u"", u"name");
1129     if (nameAttrIter == endAttrIter || nameAttrIter->value.empty()) {
1130         mLogger.error(parser->getLineNumber())
1131                 << "<item> must have a 'name' attribute."
1132                 << std::endl;
1133         return false;
1134     }
1135 
1136     ResourceName key;
1137     if (!parseXmlAttributeName(nameAttrIter->value, &key)) {
1138         mLogger.error(parser->getLineNumber())
1139                 << "invalid attribute name '"
1140                 << nameAttrIter->value
1141                 << "'."
1142                 << std::endl;
1143         return false;
1144     }
1145 
1146     if (!key.package.empty()) {
1147         // We have a package name set, so lookup its alias.
1148         parser->applyPackageAlias(&key.package, mTable->getPackage());
1149     } else {
1150         // The package name was omitted, so use the default package name with
1151         // no alias lookup.
1152         key.package = mTable->getPackage();
1153     }
1154 
1155     std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
1156     if (!value) {
1157         return false;
1158     }
1159 
1160     style.entries.push_back(Style::Entry{ Reference(key), std::move(value) });
1161     return true;
1162 }
1163 
parseStyle(XmlPullParser * parser,const ResourceNameRef & resourceName)1164 bool ResourceParser::parseStyle(XmlPullParser* parser, const ResourceNameRef& resourceName) {
1165     const SourceLine source = mSource.line(parser->getLineNumber());
1166     std::unique_ptr<Style> style = util::make_unique<Style>();
1167 
1168     const auto endAttrIter = parser->endAttributes();
1169     const auto parentAttrIter = parser->findAttribute(u"", u"parent");
1170     if (parentAttrIter != endAttrIter) {
1171         std::string errStr;
1172         if (!parseStyleParentReference(parentAttrIter->value, &style->parent, &errStr)) {
1173             mLogger.error(source.line) << errStr << "." << std::endl;
1174             return false;
1175         }
1176 
1177         if (!style->parent.name.package.empty()) {
1178             // Try to interpret the package name as an alias. These take precedence.
1179             parser->applyPackageAlias(&style->parent.name.package, mTable->getPackage());
1180         } else {
1181             // If no package is specified, this can not be an alias and is the local package.
1182             style->parent.name.package = mTable->getPackage();
1183         }
1184     } else {
1185         // No parent was specified, so try inferring it from the style name.
1186         std::u16string styleName = resourceName.entry.toString();
1187         size_t pos = styleName.find_last_of(u'.');
1188         if (pos != std::string::npos) {
1189             style->parentInferred = true;
1190             style->parent.name.package = mTable->getPackage();
1191             style->parent.name.type = ResourceType::kStyle;
1192             style->parent.name.entry = styleName.substr(0, pos);
1193         }
1194     }
1195 
1196     bool success = true;
1197     while (XmlPullParser::isGoodEvent(parser->next())) {
1198         if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
1199             continue;
1200         }
1201 
1202         ScopedXmlPullParser childParser(parser);
1203         const std::u16string& name = childParser.getElementName();
1204         if (name == u"item") {
1205             success &= parseUntypedItem(&childParser, *style);
1206         } else {
1207             mLogger.error(childParser.getLineNumber())
1208                     << "unexpected tag <"
1209                     << name
1210                     << "> in <style> resource."
1211                     << std::endl;
1212             success = false;
1213         }
1214     }
1215 
1216     if (!success) {
1217         return false;
1218     }
1219 
1220     return mTable->addResource(resourceName, mConfig, source, std::move(style));
1221 }
1222 
parseArray(XmlPullParser * parser,const ResourceNameRef & resourceName,uint32_t typeMask)1223 bool ResourceParser::parseArray(XmlPullParser* parser, const ResourceNameRef& resourceName,
1224                                 uint32_t typeMask) {
1225     const SourceLine source = mSource.line(parser->getLineNumber());
1226     std::unique_ptr<Array> array = util::make_unique<Array>();
1227 
1228     bool error = false;
1229     while (XmlPullParser::isGoodEvent(parser->next())) {
1230         if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
1231             continue;
1232         }
1233 
1234         ScopedXmlPullParser childParser(parser);
1235 
1236         if (childParser.getElementName() != u"item") {
1237             mLogger.error(childParser.getLineNumber())
1238                     << "unexpected tag <"
1239                     << childParser.getElementName()
1240                     << "> in <array> resource."
1241                     << std::endl;
1242             error = true;
1243             continue;
1244         }
1245 
1246         std::unique_ptr<Item> item = parseXml(&childParser, typeMask, kNoRawString);
1247         if (!item) {
1248             error = true;
1249             continue;
1250         }
1251         array->items.emplace_back(std::move(item));
1252     }
1253 
1254     if (error) {
1255         return false;
1256     }
1257 
1258     return mTable->addResource(resourceName, mConfig, source, std::move(array));
1259 }
1260 
parsePlural(XmlPullParser * parser,const ResourceNameRef & resourceName)1261 bool ResourceParser::parsePlural(XmlPullParser* parser, const ResourceNameRef& resourceName) {
1262     const SourceLine source = mSource.line(parser->getLineNumber());
1263     std::unique_ptr<Plural> plural = util::make_unique<Plural>();
1264 
1265     bool success = true;
1266     while (XmlPullParser::isGoodEvent(parser->next())) {
1267         if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
1268             continue;
1269         }
1270 
1271         ScopedXmlPullParser childParser(parser);
1272 
1273         if (!childParser.getElementNamespace().empty() ||
1274                 childParser.getElementName() != u"item") {
1275             success = false;
1276             continue;
1277         }
1278 
1279         const auto endAttrIter = childParser.endAttributes();
1280         auto attrIter = childParser.findAttribute(u"", u"quantity");
1281         if (attrIter == endAttrIter || attrIter->value.empty()) {
1282             mLogger.error(childParser.getLineNumber())
1283                     << "<item> in <plurals> requires attribute 'quantity'."
1284                     << std::endl;
1285             success = false;
1286             continue;
1287         }
1288 
1289         StringPiece16 trimmedQuantity = util::trimWhitespace(attrIter->value);
1290         size_t index = 0;
1291         if (trimmedQuantity == u"zero") {
1292             index = Plural::Zero;
1293         } else if (trimmedQuantity == u"one") {
1294             index = Plural::One;
1295         } else if (trimmedQuantity == u"two") {
1296             index = Plural::Two;
1297         } else if (trimmedQuantity == u"few") {
1298             index = Plural::Few;
1299         } else if (trimmedQuantity == u"many") {
1300             index = Plural::Many;
1301         } else if (trimmedQuantity == u"other") {
1302             index = Plural::Other;
1303         } else {
1304             mLogger.error(childParser.getLineNumber())
1305                     << "<item> in <plural> has invalid value '"
1306                     << trimmedQuantity
1307                     << "' for attribute 'quantity'."
1308                     << std::endl;
1309             success = false;
1310             continue;
1311         }
1312 
1313         if (plural->values[index]) {
1314             mLogger.error(childParser.getLineNumber())
1315                     << "duplicate quantity '"
1316                     << trimmedQuantity
1317                     << "'."
1318                     << std::endl;
1319             success = false;
1320             continue;
1321         }
1322 
1323         if (!(plural->values[index] = parseXml(&childParser, android::ResTable_map::TYPE_STRING,
1324                                                kNoRawString))) {
1325             success = false;
1326         }
1327     }
1328 
1329     if (!success) {
1330         return false;
1331     }
1332 
1333     return mTable->addResource(resourceName, mConfig, source, std::move(plural));
1334 }
1335 
parseDeclareStyleable(XmlPullParser * parser,const ResourceNameRef & resourceName)1336 bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser,
1337                                            const ResourceNameRef& resourceName) {
1338     const SourceLine source = mSource.line(parser->getLineNumber());
1339     std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
1340 
1341     bool success = true;
1342     while (XmlPullParser::isGoodEvent(parser->next())) {
1343         if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
1344             continue;
1345         }
1346 
1347         ScopedXmlPullParser childParser(parser);
1348 
1349         const std::u16string& elementName = childParser.getElementName();
1350         if (elementName == u"attr") {
1351             const auto endAttrIter = childParser.endAttributes();
1352             auto attrIter = childParser.findAttribute(u"", u"name");
1353             if (attrIter == endAttrIter || attrIter->value.empty()) {
1354                 mLogger.error(childParser.getLineNumber())
1355                         << "<attr> tag must have a 'name' attribute."
1356                         << std::endl;
1357                 success = false;
1358                 continue;
1359             }
1360 
1361             // Copy because our iterator will be invalidated.
1362             ResourceName attrResourceName = {
1363                     mTable->getPackage(),
1364                     ResourceType::kAttr,
1365                     attrIter->value
1366             };
1367 
1368             std::unique_ptr<Attribute> attr = parseAttrImpl(&childParser, &attrResourceName, true);
1369             if (!attr) {
1370                 success = false;
1371                 continue;
1372             }
1373 
1374             styleable->entries.emplace_back(attrResourceName);
1375 
1376             // The package may have been corrected to another package. If that is so,
1377             // we don't add the declaration.
1378             if (attrResourceName.package == mTable->getPackage()) {
1379                 success &= mTable->addResource(attrResourceName, mConfig,
1380                                                mSource.line(childParser.getLineNumber()),
1381                                                std::move(attr));
1382             }
1383 
1384         } else if (elementName != u"eat-comment" && elementName != u"skip") {
1385             mLogger.error(childParser.getLineNumber())
1386                     << "<"
1387                     << elementName
1388                     << "> is not allowed inside <declare-styleable>."
1389                     << std::endl;
1390             success = false;
1391         }
1392     }
1393 
1394     if (!success) {
1395         return false;
1396     }
1397 
1398     return mTable->addResource(resourceName, mConfig, source, std::move(styleable));
1399 }
1400 
1401 } // namespace aapt
1402