blob: d2eccbc4e90e297bfd7810d8da7ee0b922816bc0 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
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 "ResourceTable.h"
18#include "ResourceUtils.h"
19#include "ResourceValues.h"
20#include "Source.h"
21#include "ValueVisitor.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070022#include "unflatten/BinaryResourceParser.h"
23#include "unflatten/ResChunkPullParser.h"
24#include "util/Util.h"
25
26#include <androidfw/ResourceTypes.h>
27#include <androidfw/TypeWrappers.h>
Elliott Hughese7d2cc32015-12-08 08:57:14 -080028#include <android-base/macros.h>
Adam Lesinski803c7c82016-04-06 16:09:43 -070029#include <algorithm>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070030#include <map>
31#include <string>
32
33namespace aapt {
34
35using namespace android;
36
Adam Lesinski59e04c62016-02-04 15:59:23 -080037namespace {
38
Adam Lesinski1ab598f2015-08-14 14:26:04 -070039/*
40 * Visitor that converts a reference's resource ID to a resource name,
41 * given a mapping from resource ID to resource name.
42 */
43class ReferenceIdToNameVisitor : public ValueVisitor {
44private:
45 const std::map<ResourceId, ResourceName>* mMapping;
46
47public:
48 using ValueVisitor::visit;
49
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -070050 explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping) :
Adam Lesinski1ab598f2015-08-14 14:26:04 -070051 mMapping(mapping) {
52 assert(mMapping);
53 }
54
55 void visit(Reference* reference) override {
56 if (!reference->id || !reference->id.value().isValid()) {
57 return;
58 }
59
60 ResourceId id = reference->id.value();
61 auto cacheIter = mMapping->find(id);
62 if (cacheIter != mMapping->end()) {
63 reference->name = cacheIter->second;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070064 }
65 }
66};
67
Adam Lesinski59e04c62016-02-04 15:59:23 -080068} // namespace
69
Adam Lesinski1ab598f2015-08-14 14:26:04 -070070BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table,
71 const Source& source, const void* data, size_t len) :
72 mContext(context), mTable(table), mSource(source), mData(data), mDataLen(len) {
73}
74
75bool BinaryResourceParser::parse() {
76 ResChunkPullParser parser(mData, mDataLen);
77
78 bool error = false;
79 while(ResChunkPullParser::isGoodEvent(parser.next())) {
80 if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
81 mContext->getDiagnostics()->warn(DiagMessage(mSource)
82 << "unknown chunk of type '"
83 << (int) parser.getChunk()->type << "'");
84 continue;
85 }
86
87 if (!parseTable(parser.getChunk())) {
88 error = true;
89 }
90 }
91
92 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
93 mContext->getDiagnostics()->error(DiagMessage(mSource)
94 << "corrupt resource table: "
95 << parser.getLastError());
96 return false;
97 }
98 return !error;
99}
100
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700101/**
102 * Parses the resource table, which contains all the packages, types, and entries.
103 */
104bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
105 const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
106 if (!tableHeader) {
107 mContext->getDiagnostics()->error(DiagMessage(mSource) << "corrupt ResTable_header chunk");
108 return false;
109 }
110
111 ResChunkPullParser parser(getChunkData(&tableHeader->header),
112 getChunkDataLen(&tableHeader->header));
113 while (ResChunkPullParser::isGoodEvent(parser.next())) {
114 switch (util::deviceToHost16(parser.getChunk()->type)) {
115 case android::RES_STRING_POOL_TYPE:
116 if (mValuePool.getError() == NO_INIT) {
117 status_t err = mValuePool.setTo(parser.getChunk(),
118 util::deviceToHost32(parser.getChunk()->size));
119 if (err != NO_ERROR) {
120 mContext->getDiagnostics()->error(DiagMessage(mSource)
121 << "corrupt string pool in ResTable: "
122 << mValuePool.getError());
123 return false;
124 }
125
126 // Reserve some space for the strings we are going to add.
127 mTable->stringPool.hintWillAdd(mValuePool.size(), mValuePool.styleCount());
128 } else {
129 mContext->getDiagnostics()->warn(DiagMessage(mSource)
130 << "unexpected string pool in ResTable");
131 }
132 break;
133
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700134 case android::RES_TABLE_PACKAGE_TYPE:
135 if (!parsePackage(parser.getChunk())) {
136 return false;
137 }
138 break;
139
140 default:
141 mContext->getDiagnostics()
142 ->warn(DiagMessage(mSource)
143 << "unexpected chunk type "
144 << (int) util::deviceToHost16(parser.getChunk()->type));
145 break;
146 }
147 }
148
149 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
150 mContext->getDiagnostics()->error(DiagMessage(mSource)
151 << "corrupt resource table: " << parser.getLastError());
152 return false;
153 }
154 return true;
155}
156
157
158bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
159 const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
160 if (!packageHeader) {
161 mContext->getDiagnostics()->error(DiagMessage(mSource)
162 << "corrupt ResTable_package chunk");
163 return false;
164 }
165
166 uint32_t packageId = util::deviceToHost32(packageHeader->id);
167 if (packageId > std::numeric_limits<uint8_t>::max()) {
168 mContext->getDiagnostics()->error(DiagMessage(mSource)
169 << "package ID is too big (" << packageId << ")");
170 return false;
171 }
172
173 // Extract the package name.
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700174 size_t len = strnlen16((const char16_t*) packageHeader->name, arraysize(packageHeader->name));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700175 std::u16string packageName;
176 packageName.resize(len);
177 for (size_t i = 0; i < len; i++) {
178 packageName[i] = util::deviceToHost16(packageHeader->name[i]);
179 }
180
181 ResourceTablePackage* package = mTable->createPackage(packageName, (uint8_t) packageId);
182 if (!package) {
183 mContext->getDiagnostics()->error(DiagMessage(mSource)
184 << "incompatible package '" << packageName
185 << "' with ID " << packageId);
186 return false;
187 }
188
Adam Lesinskie352b992015-11-16 11:59:14 -0800189 // There can be multiple packages in a table, so
190 // clear the type and key pool in case they were set from a previous package.
191 mTypePool.uninit();
192 mKeyPool.uninit();
193
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700194 ResChunkPullParser parser(getChunkData(&packageHeader->header),
195 getChunkDataLen(&packageHeader->header));
196 while (ResChunkPullParser::isGoodEvent(parser.next())) {
197 switch (util::deviceToHost16(parser.getChunk()->type)) {
198 case android::RES_STRING_POOL_TYPE:
199 if (mTypePool.getError() == NO_INIT) {
200 status_t err = mTypePool.setTo(parser.getChunk(),
201 util::deviceToHost32(parser.getChunk()->size));
202 if (err != NO_ERROR) {
203 mContext->getDiagnostics()->error(DiagMessage(mSource)
204 << "corrupt type string pool in "
205 << "ResTable_package: "
206 << mTypePool.getError());
207 return false;
208 }
209 } else if (mKeyPool.getError() == NO_INIT) {
210 status_t err = mKeyPool.setTo(parser.getChunk(),
211 util::deviceToHost32(parser.getChunk()->size));
212 if (err != NO_ERROR) {
213 mContext->getDiagnostics()->error(DiagMessage(mSource)
214 << "corrupt key string pool in "
215 << "ResTable_package: "
216 << mKeyPool.getError());
217 return false;
218 }
219 } else {
220 mContext->getDiagnostics()->warn(DiagMessage(mSource) << "unexpected string pool");
221 }
222 break;
223
224 case android::RES_TABLE_TYPE_SPEC_TYPE:
225 if (!parseTypeSpec(parser.getChunk())) {
226 return false;
227 }
228 break;
229
230 case android::RES_TABLE_TYPE_TYPE:
231 if (!parseType(package, parser.getChunk())) {
232 return false;
233 }
234 break;
235
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700236 default:
237 mContext->getDiagnostics()
238 ->warn(DiagMessage(mSource)
239 << "unexpected chunk type "
240 << (int) util::deviceToHost16(parser.getChunk()->type));
241 break;
242 }
243 }
244
245 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
246 mContext->getDiagnostics()->error(DiagMessage(mSource)
247 << "corrupt ResTable_package: "
248 << parser.getLastError());
249 return false;
250 }
251
252 // Now go through the table and change local resource ID references to
253 // symbolic references.
254 ReferenceIdToNameVisitor visitor(&mIdIndex);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800255 visitAllValuesInTable(mTable, &visitor);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700256 return true;
257}
258
259bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
260 if (mTypePool.getError() != NO_ERROR) {
261 mContext->getDiagnostics()->error(DiagMessage(mSource)
262 << "missing type string pool");
263 return false;
264 }
265
266 const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
267 if (!typeSpec) {
268 mContext->getDiagnostics()->error(DiagMessage(mSource)
269 << "corrupt ResTable_typeSpec chunk");
270 return false;
271 }
272
273 if (typeSpec->id == 0) {
274 mContext->getDiagnostics()->error(DiagMessage(mSource)
275 << "ResTable_typeSpec has invalid id: " << typeSpec->id);
276 return false;
277 }
278 return true;
279}
280
281bool BinaryResourceParser::parseType(const ResourceTablePackage* package,
282 const ResChunk_header* chunk) {
283 if (mTypePool.getError() != NO_ERROR) {
284 mContext->getDiagnostics()->error(DiagMessage(mSource)
285 << "missing type string pool");
286 return false;
287 }
288
289 if (mKeyPool.getError() != NO_ERROR) {
290 mContext->getDiagnostics()->error(DiagMessage(mSource)
291 << "missing key string pool");
292 return false;
293 }
294
295 const ResTable_type* type = convertTo<ResTable_type>(chunk);
296 if (!type) {
297 mContext->getDiagnostics()->error(DiagMessage(mSource)
298 << "corrupt ResTable_type chunk");
299 return false;
300 }
301
302 if (type->id == 0) {
303 mContext->getDiagnostics()->error(DiagMessage(mSource)
304 << "ResTable_type has invalid id: " << (int) type->id);
305 return false;
306 }
307
308 ConfigDescription config;
309 config.copyFromDtoH(type->config);
310
311 StringPiece16 typeStr16 = util::getString(mTypePool, type->id - 1);
312
313 const ResourceType* parsedType = parseResourceType(typeStr16);
314 if (!parsedType) {
315 mContext->getDiagnostics()->error(DiagMessage(mSource)
316 << "invalid type name '" << typeStr16
317 << "' for type with ID " << (int) type->id);
318 return false;
319 }
320
321 TypeVariant tv(type);
322 for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
323 const ResTable_entry* entry = *it;
324 if (!entry) {
325 continue;
326 }
327
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700328 const ResourceName name(package->name, *parsedType,
329 util::getString(mKeyPool,
330 util::deviceToHost32(entry->key.index)).toString());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700331
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700332 const ResourceId resId(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700333
334 std::unique_ptr<Value> resourceValue;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700335 if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
336 const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700337
338 // TODO(adamlesinski): Check that the entry count is valid.
339 resourceValue = parseMapEntry(name, config, mapEntry);
340 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700341 const Res_value* value = (const Res_value*)(
342 (const uint8_t*) entry + util::deviceToHost32(entry->size));
343 resourceValue = parseValue(name, config, value, entry->flags);
344 }
345
Adam Lesinski467f1712015-11-16 17:35:44 -0800346 if (!resourceValue) {
347 mContext->getDiagnostics()->error(DiagMessage(mSource)
348 << "failed to parse value for resource " << name
349 << " (" << resId << ") with configuration '"
350 << config << "'");
351 return false;
352 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700353
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800354 if (!mTable->addResourceAllowMangled(name, config, {}, std::move(resourceValue),
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700355 mContext->getDiagnostics())) {
356 return false;
357 }
358
359 if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700360 Symbol symbol;
361 symbol.state = SymbolState::kPublic;
362 symbol.source = mSource.withLine(0);
363 if (!mTable->setSymbolStateAllowMangled(name, resId, symbol,
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700364 mContext->getDiagnostics())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700365 return false;
366 }
367 }
368
369 // Add this resource name->id mapping to the index so
370 // that we can resolve all ID references to name references.
371 auto cacheIter = mIdIndex.find(resId);
372 if (cacheIter == mIdIndex.end()) {
373 mIdIndex.insert({ resId, name });
374 }
375 }
376 return true;
377}
378
379std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
380 const ConfigDescription& config,
381 const Res_value* value,
382 uint16_t flags) {
383 if (name.type == ResourceType::kId) {
384 return util::make_unique<Id>();
385 }
386
387 const uint32_t data = util::deviceToHost32(value->data);
388
389 if (value->dataType == Res_value::TYPE_STRING) {
390 StringPiece16 str = util::getString(mValuePool, data);
391
392 const ResStringPool_span* spans = mValuePool.styleAt(data);
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700393
394 // Check if the string has a valid style associated with it.
395 if (spans != nullptr && spans->name.index != ResStringPool_span::END) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700396 StyleString styleStr = { str.toString() };
397 while (spans->name.index != ResStringPool_span::END) {
398 styleStr.spans.push_back(Span{
399 util::getString(mValuePool, spans->name.index).toString(),
400 spans->firstChar,
401 spans->lastChar
402 });
403 spans++;
404 }
405 return util::make_unique<StyledString>(mTable->stringPool.makeRef(
406 styleStr, StringPool::Context{1, config}));
407 } else {
408 if (name.type != ResourceType::kString &&
409 util::stringStartsWith<char16_t>(str, u"res/")) {
410 // This must be a FileReference.
411 return util::make_unique<FileReference>(mTable->stringPool.makeRef(
412 str, StringPool::Context{ 0, config }));
413 }
414
415 // There are no styles associated with this string, so treat it as
416 // a simple string.
417 return util::make_unique<String>(mTable->stringPool.makeRef(
418 str, StringPool::Context{1, config}));
419 }
420 }
421
422 if (value->dataType == Res_value::TYPE_REFERENCE ||
423 value->dataType == Res_value::TYPE_ATTRIBUTE) {
424 const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
Adam Lesinski467f1712015-11-16 17:35:44 -0800425 Reference::Type::kResource : Reference::Type::kAttribute;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700426
Adam Lesinski59e04c62016-02-04 15:59:23 -0800427 if (data == 0) {
428 // A reference of 0, must be the magic @null reference.
429 Res_value nullType = {};
430 nullType.dataType = Res_value::TYPE_REFERENCE;
431 return util::make_unique<BinaryPrimitive>(nullType);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700432 }
433
Adam Lesinski59e04c62016-02-04 15:59:23 -0800434 // This is a normal reference.
435 return util::make_unique<Reference>(data, type);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700436 }
437
438 // Treat this as a raw binary primitive.
439 return util::make_unique<BinaryPrimitive>(*value);
440}
441
442std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
443 const ConfigDescription& config,
444 const ResTable_map_entry* map) {
445 switch (name.type) {
446 case ResourceType::kStyle:
447 return parseStyle(name, config, map);
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700448 case ResourceType::kAttrPrivate:
449 // fallthrough
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700450 case ResourceType::kAttr:
451 return parseAttr(name, config, map);
452 case ResourceType::kArray:
453 return parseArray(name, config, map);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700454 case ResourceType::kPlurals:
455 return parsePlural(name, config, map);
456 default:
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700457 assert(false && "unknown map type");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700458 break;
459 }
460 return {};
461}
462
463std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
464 const ConfigDescription& config,
465 const ResTable_map_entry* map) {
466 std::unique_ptr<Style> style = util::make_unique<Style>();
Adam Lesinski59e04c62016-02-04 15:59:23 -0800467 if (util::deviceToHost32(map->parent.ident) != 0) {
468 // The parent is a regular reference to a resource.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700469 style->parent = Reference(util::deviceToHost32(map->parent.ident));
470 }
471
472 for (const ResTable_map& mapEntry : map) {
Adam Lesinski28cacf02015-11-23 14:22:47 -0800473 if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
Adam Lesinski28cacf02015-11-23 14:22:47 -0800474 continue;
475 }
476
Adam Lesinski59e04c62016-02-04 15:59:23 -0800477 Style::Entry styleEntry;
478 styleEntry.key = Reference(util::deviceToHost32(mapEntry.name.ident));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700479 styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
Adam Lesinski467f1712015-11-16 17:35:44 -0800480 if (!styleEntry.value) {
481 return {};
482 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800483 style->entries.push_back(std::move(styleEntry));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700484 }
485 return style;
486}
487
488std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
489 const ConfigDescription& config,
490 const ResTable_map_entry* map) {
491 const bool isWeak = (util::deviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
492 std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
493
494 // First we must discover what type of attribute this is. Find the type mask.
495 auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
496 return util::deviceToHost32(entry.name.ident) == ResTable_map::ATTR_TYPE;
497 });
498
499 if (typeMaskIter != end(map)) {
500 attr->typeMask = util::deviceToHost32(typeMaskIter->value.data);
501 }
502
Adam Lesinskia5870652015-11-20 15:32:30 -0800503 for (const ResTable_map& mapEntry : map) {
504 if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
505 switch (util::deviceToHost32(mapEntry.name.ident)) {
506 case ResTable_map::ATTR_MIN:
507 attr->minInt = static_cast<int32_t>(mapEntry.value.data);
508 break;
509 case ResTable_map::ATTR_MAX:
510 attr->maxInt = static_cast<int32_t>(mapEntry.value.data);
511 break;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700512 }
Adam Lesinskia5870652015-11-20 15:32:30 -0800513 continue;
514 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700515
Adam Lesinskia5870652015-11-20 15:32:30 -0800516 if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700517 Attribute::Symbol symbol;
518 symbol.value = util::deviceToHost32(mapEntry.value.data);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800519 symbol.symbol = Reference(util::deviceToHost32(mapEntry.name.ident));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700520 attr->symbols.push_back(std::move(symbol));
521 }
522 }
523
Adam Lesinskia5870652015-11-20 15:32:30 -0800524 // TODO(adamlesinski): Find i80n, attributes.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700525 return attr;
526}
527
528std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
529 const ConfigDescription& config,
530 const ResTable_map_entry* map) {
531 std::unique_ptr<Array> array = util::make_unique<Array>();
532 for (const ResTable_map& mapEntry : map) {
533 array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
534 }
535 return array;
536}
537
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700538std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
539 const ConfigDescription& config,
540 const ResTable_map_entry* map) {
541 std::unique_ptr<Plural> plural = util::make_unique<Plural>();
542 for (const ResTable_map& mapEntry : map) {
543 std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
Adam Lesinski467f1712015-11-16 17:35:44 -0800544 if (!item) {
545 return {};
546 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700547
548 switch (util::deviceToHost32(mapEntry.name.ident)) {
Adam Lesinskia5870652015-11-20 15:32:30 -0800549 case ResTable_map::ATTR_ZERO:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700550 plural->values[Plural::Zero] = std::move(item);
551 break;
Adam Lesinskia5870652015-11-20 15:32:30 -0800552 case ResTable_map::ATTR_ONE:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700553 plural->values[Plural::One] = std::move(item);
554 break;
Adam Lesinskia5870652015-11-20 15:32:30 -0800555 case ResTable_map::ATTR_TWO:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700556 plural->values[Plural::Two] = std::move(item);
557 break;
Adam Lesinskia5870652015-11-20 15:32:30 -0800558 case ResTable_map::ATTR_FEW:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700559 plural->values[Plural::Few] = std::move(item);
560 break;
Adam Lesinskia5870652015-11-20 15:32:30 -0800561 case ResTable_map::ATTR_MANY:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700562 plural->values[Plural::Many] = std::move(item);
563 break;
Adam Lesinskia5870652015-11-20 15:32:30 -0800564 case ResTable_map::ATTR_OTHER:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700565 plural->values[Plural::Other] = std::move(item);
566 break;
567 }
568 }
569 return plural;
570}
571
572} // namespace aapt