]> git.saurik.com Git - android/aapt.git/blame - ResourceTable.cpp
Add package-verifier to aapt dump badging
[android/aapt.git] / ResourceTable.cpp
CommitLineData
a534180c
TAOSP
1//
2// Copyright 2006 The Android Open Source Project
3//
4// Build resource files from raw assets.
5//
6
7#include "ResourceTable.h"
8
9#include "XMLNode.h"
10
11#include <utils/ByteOrder.h>
12#include <utils/ResourceTypes.h>
13#include <stdarg.h>
14
15#define NOISY(x) //x
16
17status_t compileXmlFile(const sp<AaptAssets>& assets,
18 const sp<AaptFile>& target,
19 ResourceTable* table,
20 int options)
21{
22 sp<XMLNode> root = XMLNode::parse(target);
23 if (root == NULL) {
24 return UNKNOWN_ERROR;
25 }
7e486e33
DH
26
27 return compileXmlFile(assets, root, target, table, options);
28}
29
1423619a
DH
30status_t compileXmlFile(const sp<AaptAssets>& assets,
31 const sp<AaptFile>& target,
32 const sp<AaptFile>& outTarget,
33 ResourceTable* table,
34 int options)
35{
36 sp<XMLNode> root = XMLNode::parse(target);
37 if (root == NULL) {
38 return UNKNOWN_ERROR;
39 }
40
41 return compileXmlFile(assets, root, outTarget, table, options);
42}
43
7e486e33
DH
44status_t compileXmlFile(const sp<AaptAssets>& assets,
45 const sp<XMLNode>& root,
46 const sp<AaptFile>& target,
47 ResourceTable* table,
48 int options)
49{
a534180c
TAOSP
50 if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) {
51 root->removeWhitespace(true, NULL);
52 } else if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) {
53 root->removeWhitespace(false, NULL);
54 }
55
15c62a5b
KR
56 if ((options&XML_COMPILE_UTF8) != 0) {
57 root->setUTF8(true);
58 }
59
a534180c
TAOSP
60 bool hasErrors = false;
61
62 if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
63 status_t err = root->assignResourceIds(assets, table);
64 if (err != NO_ERROR) {
65 hasErrors = true;
66 }
67 }
68
69 status_t err = root->parseValues(assets, table);
70 if (err != NO_ERROR) {
71 hasErrors = true;
72 }
73
74 if (hasErrors) {
75 return UNKNOWN_ERROR;
76 }
77
78 NOISY(printf("Input XML Resource:\n"));
79 NOISY(root->print());
80 err = root->flatten(target,
81 (options&XML_COMPILE_STRIP_COMMENTS) != 0,
82 (options&XML_COMPILE_STRIP_RAW_VALUES) != 0);
83 if (err != NO_ERROR) {
84 return err;
85 }
86
87 NOISY(printf("Output XML Resource:\n"));
88 NOISY(ResXMLTree tree;
89 tree.setTo(target->getData(), target->getSize());
90 printXMLBlock(&tree));
91
92 target->setCompressionMethod(ZipEntry::kCompressDeflated);
93
94 return err;
95}
96
97#undef NOISY
98#define NOISY(x) //x
99
100struct flag_entry
101{
102 const char16_t* name;
103 size_t nameLen;
104 uint32_t value;
105 const char* description;
106};
107
108static const char16_t referenceArray[] =
109 { 'r', 'e', 'f', 'e', 'r', 'e', 'n', 'c', 'e' };
110static const char16_t stringArray[] =
111 { 's', 't', 'r', 'i', 'n', 'g' };
112static const char16_t integerArray[] =
113 { 'i', 'n', 't', 'e', 'g', 'e', 'r' };
114static const char16_t booleanArray[] =
115 { 'b', 'o', 'o', 'l', 'e', 'a', 'n' };
116static const char16_t colorArray[] =
117 { 'c', 'o', 'l', 'o', 'r' };
118static const char16_t floatArray[] =
119 { 'f', 'l', 'o', 'a', 't' };
120static const char16_t dimensionArray[] =
121 { 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n' };
122static const char16_t fractionArray[] =
123 { 'f', 'r', 'a', 'c', 't', 'i', 'o', 'n' };
124static const char16_t enumArray[] =
125 { 'e', 'n', 'u', 'm' };
126static const char16_t flagsArray[] =
127 { 'f', 'l', 'a', 'g', 's' };
128
129static const flag_entry gFormatFlags[] = {
130 { referenceArray, sizeof(referenceArray)/2, ResTable_map::TYPE_REFERENCE,
131 "a reference to another resource, in the form \"<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>\"\n"
132 "or to a theme attribute in the form \"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\"."},
133 { stringArray, sizeof(stringArray)/2, ResTable_map::TYPE_STRING,
134 "a string value, using '\\\\;' to escape characters such as '\\\\n' or '\\\\uxxxx' for a unicode character." },
135 { integerArray, sizeof(integerArray)/2, ResTable_map::TYPE_INTEGER,
136 "an integer value, such as \"<code>100</code>\"." },
137 { booleanArray, sizeof(booleanArray)/2, ResTable_map::TYPE_BOOLEAN,
138 "a boolean value, either \"<code>true</code>\" or \"<code>false</code>\"." },
139 { colorArray, sizeof(colorArray)/2, ResTable_map::TYPE_COLOR,
140 "a color value, in the form of \"<code>#<i>rgb</i></code>\", \"<code>#<i>argb</i></code>\",\n"
141 "\"<code>#<i>rrggbb</i></code>\", or \"<code>#<i>aarrggbb</i></code>\"." },
142 { floatArray, sizeof(floatArray)/2, ResTable_map::TYPE_FLOAT,
143 "a floating point value, such as \"<code>1.2</code>\"."},
144 { dimensionArray, sizeof(dimensionArray)/2, ResTable_map::TYPE_DIMENSION,
145 "a dimension value, which is a floating point number appended with a unit such as \"<code>14.5sp</code>\".\n"
146 "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n"
147 "in (inches), mm (millimeters)." },
148 { fractionArray, sizeof(fractionArray)/2, ResTable_map::TYPE_FRACTION,
149 "a fractional value, which is a floating point number appended with either % or %p, such as \"<code>14.5%</code>\".\n"
150 "The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to\n"
151 "some parent container." },
152 { enumArray, sizeof(enumArray)/2, ResTable_map::TYPE_ENUM, NULL },
153 { flagsArray, sizeof(flagsArray)/2, ResTable_map::TYPE_FLAGS, NULL },
154 { NULL, 0, 0, NULL }
155};
156
157static const char16_t suggestedArray[] = { 's', 'u', 'g', 'g', 'e', 's', 't', 'e', 'd' };
158
159static const flag_entry l10nRequiredFlags[] = {
160 { suggestedArray, sizeof(suggestedArray)/2, ResTable_map::L10N_SUGGESTED, NULL },
161 { NULL, 0, 0, NULL }
162};
163
164static const char16_t nulStr[] = { 0 };
165
166static uint32_t parse_flags(const char16_t* str, size_t len,
167 const flag_entry* flags, bool* outError = NULL)
168{
169 while (len > 0 && isspace(*str)) {
170 str++;
171 len--;
172 }
173 while (len > 0 && isspace(str[len-1])) {
174 len--;
175 }
176
177 const char16_t* const end = str + len;
178 uint32_t value = 0;
179
180 while (str < end) {
181 const char16_t* div = str;
182 while (div < end && *div != '|') {
183 div++;
184 }
185
186 const flag_entry* cur = flags;
187 while (cur->name) {
188 if (strzcmp16(cur->name, cur->nameLen, str, div-str) == 0) {
189 value |= cur->value;
190 break;
191 }
192 cur++;
193 }
194
195 if (!cur->name) {
196 if (outError) *outError = true;
197 return 0;
198 }
199
200 str = div < end ? div+1 : div;
201 }
202
203 if (outError) *outError = false;
204 return value;
205}
206
207static String16 mayOrMust(int type, int flags)
208{
209 if ((type&(~flags)) == 0) {
210 return String16("<p>Must");
211 }
212
213 return String16("<p>May");
214}
215
216static void appendTypeInfo(ResourceTable* outTable, const String16& pkg,
217 const String16& typeName, const String16& ident, int type,
218 const flag_entry* flags)
219{
220 bool hadType = false;
221 while (flags->name) {
222 if ((type&flags->value) != 0 && flags->description != NULL) {
223 String16 fullMsg(mayOrMust(type, flags->value));
224 fullMsg.append(String16(" be "));
225 fullMsg.append(String16(flags->description));
226 outTable->appendTypeComment(pkg, typeName, ident, fullMsg);
227 hadType = true;
228 }
229 flags++;
230 }
231 if (hadType && (type&ResTable_map::TYPE_REFERENCE) == 0) {
232 outTable->appendTypeComment(pkg, typeName, ident,
233 String16("<p>This may also be a reference to a resource (in the form\n"
234 "\"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>\") or\n"
235 "theme attribute (in the form\n"
236 "\"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\")\n"
237 "containing a value of this type."));
238 }
239}
240
241struct PendingAttribute
242{
243 const String16 myPackage;
244 const SourcePos sourcePos;
245 const bool appendComment;
246 int32_t type;
247 String16 ident;
248 String16 comment;
249 bool hasErrors;
250 bool added;
251
252 PendingAttribute(String16 _package, const sp<AaptFile>& in,
253 ResXMLTree& block, bool _appendComment)
254 : myPackage(_package)
255 , sourcePos(in->getPrintableSource(), block.getLineNumber())
256 , appendComment(_appendComment)
257 , type(ResTable_map::TYPE_ANY)
258 , hasErrors(false)
259 , added(false)
260 {
261 }
262
263 status_t createIfNeeded(ResourceTable* outTable)
264 {
265 if (added || hasErrors) {
266 return NO_ERROR;
267 }
268 added = true;
269
270 String16 attr16("attr");
271
272 if (outTable->hasBagOrEntry(myPackage, attr16, ident)) {
273 sourcePos.error("Attribute \"%s\" has already been defined\n",
274 String8(ident).string());
275 hasErrors = true;
276 return UNKNOWN_ERROR;
277 }
278
279 char numberStr[16];
280 sprintf(numberStr, "%d", type);
281 status_t err = outTable->addBag(sourcePos, myPackage,
282 attr16, ident, String16(""),
283 String16("^type"),
284 String16(numberStr), NULL, NULL);
285 if (err != NO_ERROR) {
286 hasErrors = true;
287 return err;
288 }
289 outTable->appendComment(myPackage, attr16, ident, comment, appendComment);
290 //printf("Attribute %s comment: %s\n", String8(ident).string(),
291 // String8(comment).string());
292 return err;
293 }
294};
295
296static status_t compileAttribute(const sp<AaptFile>& in,
297 ResXMLTree& block,
298 const String16& myPackage,
299 ResourceTable* outTable,
300 String16* outIdent = NULL,
301 bool inStyleable = false)
302{
303 PendingAttribute attr(myPackage, in, block, inStyleable);
304
305 const String16 attr16("attr");
306 const String16 id16("id");
307
308 // Attribute type constants.
309 const String16 enum16("enum");
310 const String16 flag16("flag");
311
312 ResXMLTree::event_code_t code;
313 size_t len;
314 status_t err;
315
316 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
317 if (identIdx >= 0) {
318 attr.ident = String16(block.getAttributeStringValue(identIdx, &len));
319 if (outIdent) {
320 *outIdent = attr.ident;
321 }
322 } else {
323 attr.sourcePos.error("A 'name' attribute is required for <attr>\n");
324 attr.hasErrors = true;
325 }
326
327 attr.comment = String16(
328 block.getComment(&len) ? block.getComment(&len) : nulStr);
329
330 ssize_t typeIdx = block.indexOfAttribute(NULL, "format");
331 if (typeIdx >= 0) {
332 String16 typeStr = String16(block.getAttributeStringValue(typeIdx, &len));
333 attr.type = parse_flags(typeStr.string(), typeStr.size(), gFormatFlags);
334 if (attr.type == 0) {
335 attr.sourcePos.error("Tag <attr> 'format' attribute value \"%s\" not valid\n",
336 String8(typeStr).string());
337 attr.hasErrors = true;
338 }
339 attr.createIfNeeded(outTable);
340 } else if (!inStyleable) {
341 // Attribute definitions outside of styleables always define the
342 // attribute as a generic value.
343 attr.createIfNeeded(outTable);
344 }
345
346 //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).string(), attr.type);
347
348 ssize_t minIdx = block.indexOfAttribute(NULL, "min");
349 if (minIdx >= 0) {
350 String16 val = String16(block.getAttributeStringValue(minIdx, &len));
351 if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
352 attr.sourcePos.error("Tag <attr> 'min' attribute must be a number, not \"%s\"\n",
353 String8(val).string());
354 attr.hasErrors = true;
355 }
356 attr.createIfNeeded(outTable);
357 if (!attr.hasErrors) {
358 err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
359 String16(""), String16("^min"), String16(val), NULL, NULL);
360 if (err != NO_ERROR) {
361 attr.hasErrors = true;
362 }
363 }
364 }
365
366 ssize_t maxIdx = block.indexOfAttribute(NULL, "max");
367 if (maxIdx >= 0) {
368 String16 val = String16(block.getAttributeStringValue(maxIdx, &len));
369 if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
370 attr.sourcePos.error("Tag <attr> 'max' attribute must be a number, not \"%s\"\n",
371 String8(val).string());
372 attr.hasErrors = true;
373 }
374 attr.createIfNeeded(outTable);
375 if (!attr.hasErrors) {
376 err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
377 String16(""), String16("^max"), String16(val), NULL, NULL);
378 attr.hasErrors = true;
379 }
380 }
381
382 if ((minIdx >= 0 || maxIdx >= 0) && (attr.type&ResTable_map::TYPE_INTEGER) == 0) {
383 attr.sourcePos.error("Tag <attr> must have format=integer attribute if using max or min\n");
384 attr.hasErrors = true;
385 }
386
387 ssize_t l10nIdx = block.indexOfAttribute(NULL, "localization");
388 if (l10nIdx >= 0) {
389 const uint16_t* str = block.getAttributeStringValue(l10nIdx, &len);
390 bool error;
391 uint32_t l10n_required = parse_flags(str, len, l10nRequiredFlags, &error);
392 if (error) {
393 attr.sourcePos.error("Tag <attr> 'localization' attribute value \"%s\" not valid\n",
394 String8(str).string());
395 attr.hasErrors = true;
396 }
397 attr.createIfNeeded(outTable);
398 if (!attr.hasErrors) {
c011bf4d 399 char buf[11];
a534180c
TAOSP
400 sprintf(buf, "%d", l10n_required);
401 err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
402 String16(""), String16("^l10n"), String16(buf), NULL, NULL);
403 if (err != NO_ERROR) {
404 attr.hasErrors = true;
405 }
406 }
407 }
408
409 String16 enumOrFlagsComment;
410
411 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
412 if (code == ResXMLTree::START_TAG) {
413 uint32_t localType = 0;
414 if (strcmp16(block.getElementName(&len), enum16.string()) == 0) {
415 localType = ResTable_map::TYPE_ENUM;
416 } else if (strcmp16(block.getElementName(&len), flag16.string()) == 0) {
417 localType = ResTable_map::TYPE_FLAGS;
418 } else {
419 SourcePos(in->getPrintableSource(), block.getLineNumber())
420 .error("Tag <%s> can not appear inside <attr>, only <enum> or <flag>\n",
421 String8(block.getElementName(&len)).string());
422 return UNKNOWN_ERROR;
423 }
424
425 attr.createIfNeeded(outTable);
426
427 if (attr.type == ResTable_map::TYPE_ANY) {
428 // No type was explicitly stated, so supplying enum tags
429 // implicitly creates an enum or flag.
430 attr.type = 0;
431 }
432
433 if ((attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) == 0) {
434 // Wasn't originally specified as an enum, so update its type.
435 attr.type |= localType;
436 if (!attr.hasErrors) {
437 char numberStr[16];
438 sprintf(numberStr, "%d", attr.type);
439 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
440 myPackage, attr16, attr.ident, String16(""),
441 String16("^type"), String16(numberStr), NULL, NULL, true);
442 if (err != NO_ERROR) {
443 attr.hasErrors = true;
444 }
445 }
446 } else if ((uint32_t)(attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) != localType) {
447 if (localType == ResTable_map::TYPE_ENUM) {
448 SourcePos(in->getPrintableSource(), block.getLineNumber())
449 .error("<enum> attribute can not be used inside a flags format\n");
450 attr.hasErrors = true;
451 } else {
452 SourcePos(in->getPrintableSource(), block.getLineNumber())
453 .error("<flag> attribute can not be used inside a enum format\n");
454 attr.hasErrors = true;
455 }
456 }
457
458 String16 itemIdent;
459 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
460 if (itemIdentIdx >= 0) {
461 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
462 } else {
463 SourcePos(in->getPrintableSource(), block.getLineNumber())
464 .error("A 'name' attribute is required for <enum> or <flag>\n");
465 attr.hasErrors = true;
466 }
467
468 String16 value;
469 ssize_t valueIdx = block.indexOfAttribute(NULL, "value");
470 if (valueIdx >= 0) {
471 value = String16(block.getAttributeStringValue(valueIdx, &len));
472 } else {
473 SourcePos(in->getPrintableSource(), block.getLineNumber())
474 .error("A 'value' attribute is required for <enum> or <flag>\n");
475 attr.hasErrors = true;
476 }
477 if (!attr.hasErrors && !ResTable::stringToInt(value.string(), value.size(), NULL)) {
478 SourcePos(in->getPrintableSource(), block.getLineNumber())
479 .error("Tag <enum> or <flag> 'value' attribute must be a number,"
480 " not \"%s\"\n",
481 String8(value).string());
482 attr.hasErrors = true;
483 }
484
485 // Make sure an id is defined for this enum/flag identifier...
486 if (!attr.hasErrors && !outTable->hasBagOrEntry(itemIdent, &id16, &myPackage)) {
487 err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
488 myPackage, id16, itemIdent, String16(), NULL);
489 if (err != NO_ERROR) {
490 attr.hasErrors = true;
491 }
492 }
493
494 if (!attr.hasErrors) {
495 if (enumOrFlagsComment.size() == 0) {
496 enumOrFlagsComment.append(mayOrMust(attr.type,
497 ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS));
498 enumOrFlagsComment.append((attr.type&ResTable_map::TYPE_ENUM)
499 ? String16(" be one of the following constant values.")
500 : String16(" be one or more (separated by '|') of the following constant values."));
3ff93dd6 501 enumOrFlagsComment.append(String16("</p>\n<table>\n"
a534180c
TAOSP
502 "<colgroup align=\"left\" />\n"
503 "<colgroup align=\"left\" />\n"
504 "<colgroup align=\"left\" />\n"
3ff93dd6 505 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>"));
a534180c
TAOSP
506 }
507
3ff93dd6 508 enumOrFlagsComment.append(String16("\n<tr><td><code>"));
a534180c 509 enumOrFlagsComment.append(itemIdent);
3ff93dd6 510 enumOrFlagsComment.append(String16("</code></td><td>"));
a534180c 511 enumOrFlagsComment.append(value);
3ff93dd6 512 enumOrFlagsComment.append(String16("</td><td>"));
a534180c
TAOSP
513 if (block.getComment(&len)) {
514 enumOrFlagsComment.append(String16(block.getComment(&len)));
515 }
3ff93dd6 516 enumOrFlagsComment.append(String16("</td></tr>"));
a534180c
TAOSP
517
518 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
519 myPackage,
520 attr16, attr.ident, String16(""),
521 itemIdent, value, NULL, NULL, false, true);
522 if (err != NO_ERROR) {
523 attr.hasErrors = true;
524 }
525 }
526 } else if (code == ResXMLTree::END_TAG) {
527 if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
528 break;
529 }
530 if ((attr.type&ResTable_map::TYPE_ENUM) != 0) {
531 if (strcmp16(block.getElementName(&len), enum16.string()) != 0) {
532 SourcePos(in->getPrintableSource(), block.getLineNumber())
533 .error("Found tag </%s> where </enum> is expected\n",
534 String8(block.getElementName(&len)).string());
535 return UNKNOWN_ERROR;
536 }
537 } else {
538 if (strcmp16(block.getElementName(&len), flag16.string()) != 0) {
539 SourcePos(in->getPrintableSource(), block.getLineNumber())
540 .error("Found tag </%s> where </flag> is expected\n",
541 String8(block.getElementName(&len)).string());
542 return UNKNOWN_ERROR;
543 }
544 }
545 }
546 }
547
548 if (!attr.hasErrors && attr.added) {
549 appendTypeInfo(outTable, myPackage, attr16, attr.ident, attr.type, gFormatFlags);
550 }
551
552 if (!attr.hasErrors && enumOrFlagsComment.size() > 0) {
553 enumOrFlagsComment.append(String16("\n</table>"));
554 outTable->appendTypeComment(myPackage, attr16, attr.ident, enumOrFlagsComment);
555 }
556
557
558 return NO_ERROR;
559}
560
561bool localeIsDefined(const ResTable_config& config)
562{
563 return config.locale == 0;
564}
565
566status_t parseAndAddBag(Bundle* bundle,
567 const sp<AaptFile>& in,
568 ResXMLTree* block,
569 const ResTable_config& config,
570 const String16& myPackage,
571 const String16& curType,
572 const String16& ident,
573 const String16& parentIdent,
574 const String16& itemIdent,
575 int32_t curFormat,
6b12183e 576 bool isFormatted,
b8ea3a3f 577 const String16& product,
a534180c
TAOSP
578 bool pseudolocalize,
579 const bool overwrite,
580 ResourceTable* outTable)
581{
582 status_t err;
583 const String16 item16("item");
584
585 String16 str;
586 Vector<StringPool::entry_style_span> spans;
587 err = parseStyledString(bundle, in->getPrintableSource().string(),
6b12183e 588 block, item16, &str, &spans, isFormatted,
a534180c
TAOSP
589 pseudolocalize);
590 if (err != NO_ERROR) {
591 return err;
592 }
593
594 NOISY(printf("Adding resource bag entry l=%c%c c=%c%c orien=%d d=%d "
595 " pid=%s, bag=%s, id=%s: %s\n",
596 config.language[0], config.language[1],
597 config.country[0], config.country[1],
598 config.orientation, config.density,
599 String8(parentIdent).string(),
600 String8(ident).string(),
601 String8(itemIdent).string(),
602 String8(str).string()));
603
604 err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()),
605 myPackage, curType, ident, parentIdent, itemIdent, str,
606 &spans, &config, overwrite, false, curFormat);
607 return err;
608}
609
b8ea3a3f
EF
610/*
611 * Returns true if needle is one of the elements in the comma-separated list
612 * haystack, false otherwise.
613 */
614bool isInProductList(const String16& needle, const String16& haystack) {
615 const char16_t *needle2 = needle.string();
616 const char16_t *haystack2 = haystack.string();
617 size_t needlesize = needle.size();
618
619 while (*haystack2 != '\0') {
620 if (strncmp16(haystack2, needle2, needlesize) == 0) {
621 if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') {
622 return true;
623 }
624 }
625
626 while (*haystack2 != '\0' && *haystack2 != ',') {
627 haystack2++;
628 }
629 if (*haystack2 == ',') {
630 haystack2++;
631 }
632 }
633
634 return false;
635}
a534180c
TAOSP
636
637status_t parseAndAddEntry(Bundle* bundle,
638 const sp<AaptFile>& in,
639 ResXMLTree* block,
640 const ResTable_config& config,
641 const String16& myPackage,
642 const String16& curType,
643 const String16& ident,
644 const String16& curTag,
645 bool curIsStyled,
646 int32_t curFormat,
6b12183e 647 bool isFormatted,
b8ea3a3f 648 const String16& product,
a534180c
TAOSP
649 bool pseudolocalize,
650 const bool overwrite,
651 ResourceTable* outTable)
652{
653 status_t err;
654
655 String16 str;
656 Vector<StringPool::entry_style_span> spans;
657 err = parseStyledString(bundle, in->getPrintableSource().string(), block,
658 curTag, &str, curIsStyled ? &spans : NULL,
6b12183e 659 isFormatted, pseudolocalize);
a534180c
TAOSP
660
661 if (err < NO_ERROR) {
662 return err;
663 }
664
b8ea3a3f
EF
665 /*
666 * If a product type was specified on the command line
667 * and also in the string, and the two are not the same,
668 * return without adding the string.
669 */
670
671 const char *bundleProduct = bundle->getProduct();
672 if (bundleProduct == NULL) {
673 bundleProduct = "";
674 }
675
676 if (product.size() != 0) {
677 /*
678 * If the command-line-specified product is empty, only "default"
679 * matches. Other variants are skipped. This is so generation
680 * of the R.java file when the product is not known is predictable.
681 */
682
683 if (bundleProduct[0] == '\0') {
684 if (strcmp16(String16("default").string(), product.string()) != 0) {
685 return NO_ERROR;
686 }
687 } else {
688 /*
689 * The command-line product is not empty.
690 * If the product for this string is on the command-line list,
691 * it matches. "default" also matches, but only if nothing
692 * else has matched already.
693 */
694
695 if (isInProductList(product, String16(bundleProduct))) {
696 ;
697 } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
8a9f9549 698 !outTable->hasBagOrEntry(myPackage, curType, ident, config)) {
b8ea3a3f
EF
699 ;
700 } else {
701 return NO_ERROR;
702 }
703 }
704 }
705
a534180c
TAOSP
706 NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n",
707 config.language[0], config.language[1],
708 config.country[0], config.country[1],
709 config.orientation, config.density,
710 String8(ident).string(), String8(str).string()));
711
712 err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
713 myPackage, curType, ident, str, &spans, &config,
714 false, curFormat, overwrite);
715
716 return err;
717}
718
719status_t compileResourceFile(Bundle* bundle,
720 const sp<AaptAssets>& assets,
721 const sp<AaptFile>& in,
722 const ResTable_config& defParams,
723 const bool overwrite,
724 ResourceTable* outTable)
725{
726 ResXMLTree block;
727 status_t err = parseXMLResource(in, &block, false, true);
728 if (err != NO_ERROR) {
729 return err;
730 }
731
732 // Top-level tag.
733 const String16 resources16("resources");
734
735 // Identifier declaration tags.
736 const String16 declare_styleable16("declare-styleable");
737 const String16 attr16("attr");
738
739 // Data creation organizational tags.
740 const String16 string16("string");
741 const String16 drawable16("drawable");
742 const String16 color16("color");
743 const String16 bool16("bool");
744 const String16 integer16("integer");
745 const String16 dimen16("dimen");
746 const String16 fraction16("fraction");
747 const String16 style16("style");
748 const String16 plurals16("plurals");
749 const String16 array16("array");
750 const String16 string_array16("string-array");
751 const String16 integer_array16("integer-array");
752 const String16 public16("public");
3b8452c9 753 const String16 public_padding16("public-padding");
a534180c 754 const String16 private_symbols16("private-symbols");
a8c9cd56 755 const String16 add_resource16("add-resource");
a534180c
TAOSP
756 const String16 skip16("skip");
757 const String16 eat_comment16("eat-comment");
758
759 // Data creation tags.
760 const String16 bag16("bag");
761 const String16 item16("item");
762
763 // Attribute type constants.
764 const String16 enum16("enum");
765
766 // plural values
767 const String16 other16("other");
768 const String16 quantityOther16("^other");
769 const String16 zero16("zero");
770 const String16 quantityZero16("^zero");
771 const String16 one16("one");
772 const String16 quantityOne16("^one");
773 const String16 two16("two");
774 const String16 quantityTwo16("^two");
775 const String16 few16("few");
776 const String16 quantityFew16("^few");
777 const String16 many16("many");
778 const String16 quantityMany16("^many");
779
780 // useful attribute names and special values
781 const String16 name16("name");
782 const String16 translatable16("translatable");
6b12183e 783 const String16 formatted16("formatted");
a534180c
TAOSP
784 const String16 false16("false");
785
786 const String16 myPackage(assets->getPackage());
787
788 bool hasErrors = false;
6b12183e
KR
789
790 bool fileIsTranslatable = true;
791 if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) {
792 fileIsTranslatable = false;
793 }
794
3b8452c9 795 DefaultKeyedVector<String16, uint32_t> nextPublicId(0);
a534180c
TAOSP
796
797 ResXMLTree::event_code_t code;
798 do {
799 code = block.next();
800 } while (code == ResXMLTree::START_NAMESPACE);
801
802 size_t len;
803 if (code != ResXMLTree::START_TAG) {
804 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
805 "No start tag found\n");
806 return UNKNOWN_ERROR;
807 }
808 if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
809 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
810 "Invalid start tag %s\n", String8(block.getElementName(&len)).string());
811 return UNKNOWN_ERROR;
812 }
813
814 ResTable_config curParams(defParams);
815
816 ResTable_config pseudoParams(curParams);
817 pseudoParams.language[0] = 'z';
818 pseudoParams.language[1] = 'z';
819 pseudoParams.country[0] = 'Z';
820 pseudoParams.country[1] = 'Z';
821
822 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
823 if (code == ResXMLTree::START_TAG) {
824 const String16* curTag = NULL;
825 String16 curType;
826 int32_t curFormat = ResTable_map::TYPE_ANY;
827 bool curIsBag = false;
eca7446f 828 bool curIsBagReplaceOnOverwrite = false;
a534180c
TAOSP
829 bool curIsStyled = false;
830 bool curIsPseudolocalizable = false;
6b12183e 831 bool curIsFormatted = fileIsTranslatable;
a534180c
TAOSP
832 bool localHasErrors = false;
833
834 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
835 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
836 && code != ResXMLTree::BAD_DOCUMENT) {
837 if (code == ResXMLTree::END_TAG) {
838 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
839 break;
840 }
841 }
842 }
843 continue;
844
845 } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
846 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
847 && code != ResXMLTree::BAD_DOCUMENT) {
848 if (code == ResXMLTree::END_TAG) {
849 if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
850 break;
851 }
852 }
853 }
854 continue;
855
856 } else if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
857 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
858
859 String16 type;
860 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
861 if (typeIdx < 0) {
862 srcPos.error("A 'type' attribute is required for <public>\n");
863 hasErrors = localHasErrors = true;
864 }
865 type = String16(block.getAttributeStringValue(typeIdx, &len));
866
867 String16 name;
868 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
869 if (nameIdx < 0) {
870 srcPos.error("A 'name' attribute is required for <public>\n");
871 hasErrors = localHasErrors = true;
872 }
873 name = String16(block.getAttributeStringValue(nameIdx, &len));
874
875 uint32_t ident = 0;
876 ssize_t identIdx = block.indexOfAttribute(NULL, "id");
877 if (identIdx >= 0) {
878 const char16_t* identStr = block.getAttributeStringValue(identIdx, &len);
879 Res_value identValue;
880 if (!ResTable::stringToInt(identStr, len, &identValue)) {
881 srcPos.error("Given 'id' attribute is not an integer: %s\n",
882 String8(block.getAttributeStringValue(identIdx, &len)).string());
883 hasErrors = localHasErrors = true;
884 } else {
885 ident = identValue.data;
3b8452c9 886 nextPublicId.replaceValueFor(type, ident+1);
a534180c 887 }
3b8452c9 888 } else if (nextPublicId.indexOfKey(type) < 0) {
a534180c
TAOSP
889 srcPos.error("No 'id' attribute supplied <public>,"
890 " and no previous id defined in this file.\n");
891 hasErrors = localHasErrors = true;
892 } else if (!localHasErrors) {
3b8452c9
DH
893 ident = nextPublicId.valueFor(type);
894 nextPublicId.replaceValueFor(type, ident+1);
a534180c
TAOSP
895 }
896
897 if (!localHasErrors) {
898 err = outTable->addPublic(srcPos, myPackage, type, name, ident);
899 if (err < NO_ERROR) {
900 hasErrors = localHasErrors = true;
901 }
902 }
903 if (!localHasErrors) {
904 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
905 if (symbols != NULL) {
906 symbols = symbols->addNestedSymbol(String8(type), srcPos);
907 }
908 if (symbols != NULL) {
909 symbols->makeSymbolPublic(String8(name), srcPos);
910 String16 comment(
911 block.getComment(&len) ? block.getComment(&len) : nulStr);
912 symbols->appendComment(String8(name), comment, srcPos);
913 } else {
914 srcPos.error("Unable to create symbols!\n");
915 hasErrors = localHasErrors = true;
916 }
917 }
918
919 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
920 if (code == ResXMLTree::END_TAG) {
921 if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
922 break;
923 }
924 }
925 }
926 continue;
927
3b8452c9
DH
928 } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
929 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
930
931 String16 type;
932 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
933 if (typeIdx < 0) {
934 srcPos.error("A 'type' attribute is required for <public-padding>\n");
935 hasErrors = localHasErrors = true;
936 }
937 type = String16(block.getAttributeStringValue(typeIdx, &len));
938
939 String16 name;
940 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
941 if (nameIdx < 0) {
942 srcPos.error("A 'name' attribute is required for <public-padding>\n");
943 hasErrors = localHasErrors = true;
944 }
945 name = String16(block.getAttributeStringValue(nameIdx, &len));
946
947 uint32_t start = 0;
948 ssize_t startIdx = block.indexOfAttribute(NULL, "start");
949 if (startIdx >= 0) {
950 const char16_t* startStr = block.getAttributeStringValue(startIdx, &len);
951 Res_value startValue;
952 if (!ResTable::stringToInt(startStr, len, &startValue)) {
953 srcPos.error("Given 'start' attribute is not an integer: %s\n",
954 String8(block.getAttributeStringValue(startIdx, &len)).string());
955 hasErrors = localHasErrors = true;
956 } else {
957 start = startValue.data;
958 }
959 } else if (nextPublicId.indexOfKey(type) < 0) {
960 srcPos.error("No 'start' attribute supplied <public-padding>,"
961 " and no previous id defined in this file.\n");
962 hasErrors = localHasErrors = true;
963 } else if (!localHasErrors) {
964 start = nextPublicId.valueFor(type);
965 }
966
967 uint32_t end = 0;
968 ssize_t endIdx = block.indexOfAttribute(NULL, "end");
969 if (endIdx >= 0) {
970 const char16_t* endStr = block.getAttributeStringValue(endIdx, &len);
971 Res_value endValue;
972 if (!ResTable::stringToInt(endStr, len, &endValue)) {
973 srcPos.error("Given 'end' attribute is not an integer: %s\n",
974 String8(block.getAttributeStringValue(endIdx, &len)).string());
975 hasErrors = localHasErrors = true;
976 } else {
977 end = endValue.data;
978 }
979 } else {
980 srcPos.error("No 'end' attribute supplied <public-padding>\n");
981 hasErrors = localHasErrors = true;
982 }
983
984 if (end >= start) {
985 nextPublicId.replaceValueFor(type, end+1);
986 } else {
987 srcPos.error("Padding start '%ul' is after end '%ul'\n",
988 start, end);
989 hasErrors = localHasErrors = true;
990 }
991
992 String16 comment(
993 block.getComment(&len) ? block.getComment(&len) : nulStr);
994 for (uint32_t curIdent=start; curIdent<=end; curIdent++) {
995 if (localHasErrors) {
996 break;
997 }
998 String16 curName(name);
999 char buf[64];
1000 sprintf(buf, "%d", (int)(end-curIdent+1));
1001 curName.append(String16(buf));
1002
1003 err = outTable->addEntry(srcPos, myPackage, type, curName,
1004 String16("padding"), NULL, &curParams, false,
1005 ResTable_map::TYPE_STRING, overwrite);
1006 if (err < NO_ERROR) {
1007 hasErrors = localHasErrors = true;
1008 break;
1009 }
1010 err = outTable->addPublic(srcPos, myPackage, type,
1011 curName, curIdent);
1012 if (err < NO_ERROR) {
1013 hasErrors = localHasErrors = true;
1014 break;
1015 }
1016 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
1017 if (symbols != NULL) {
1018 symbols = symbols->addNestedSymbol(String8(type), srcPos);
1019 }
1020 if (symbols != NULL) {
1021 symbols->makeSymbolPublic(String8(curName), srcPos);
1022 symbols->appendComment(String8(curName), comment, srcPos);
1023 } else {
1024 srcPos.error("Unable to create symbols!\n");
1025 hasErrors = localHasErrors = true;
1026 }
1027 }
1028
1029 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1030 if (code == ResXMLTree::END_TAG) {
1031 if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
1032 break;
1033 }
1034 }
1035 }
1036 continue;
1037
a534180c
TAOSP
1038 } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
1039 String16 pkg;
1040 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
1041 if (pkgIdx < 0) {
1042 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1043 "A 'package' attribute is required for <private-symbols>\n");
1044 hasErrors = localHasErrors = true;
1045 }
1046 pkg = String16(block.getAttributeStringValue(pkgIdx, &len));
1047 if (!localHasErrors) {
1048 assets->setSymbolsPrivatePackage(String8(pkg));
1049 }
1050
1051 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1052 if (code == ResXMLTree::END_TAG) {
1053 if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
1054 break;
1055 }
1056 }
1057 }
1058 continue;
1059
a8c9cd56
DH
1060 } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
1061 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
1062
1063 String16 typeName;
1064 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
1065 if (typeIdx < 0) {
1066 srcPos.error("A 'type' attribute is required for <add-resource>\n");
1067 hasErrors = localHasErrors = true;
1068 }
1069 typeName = String16(block.getAttributeStringValue(typeIdx, &len));
1070
1071 String16 name;
1072 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
1073 if (nameIdx < 0) {
1074 srcPos.error("A 'name' attribute is required for <add-resource>\n");
1075 hasErrors = localHasErrors = true;
1076 }
1077 name = String16(block.getAttributeStringValue(nameIdx, &len));
1078
1079 outTable->canAddEntry(srcPos, myPackage, typeName, name);
1080
1081 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1082 if (code == ResXMLTree::END_TAG) {
706a572f 1083 if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
a8c9cd56
DH
1084 break;
1085 }
1086 }
1087 }
1088 continue;
1089
a534180c
TAOSP
1090 } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
1091 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
1092
1093 String16 ident;
1094 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
1095 if (identIdx < 0) {
1096 srcPos.error("A 'name' attribute is required for <declare-styleable>\n");
1097 hasErrors = localHasErrors = true;
1098 }
1099 ident = String16(block.getAttributeStringValue(identIdx, &len));
1100
1101 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
1102 if (!localHasErrors) {
1103 if (symbols != NULL) {
1104 symbols = symbols->addNestedSymbol(String8("styleable"), srcPos);
1105 }
1106 sp<AaptSymbols> styleSymbols = symbols;
1107 if (symbols != NULL) {
1108 symbols = symbols->addNestedSymbol(String8(ident), srcPos);
1109 }
1110 if (symbols == NULL) {
1111 srcPos.error("Unable to create symbols!\n");
1112 return UNKNOWN_ERROR;
1113 }
1114
1115 String16 comment(
1116 block.getComment(&len) ? block.getComment(&len) : nulStr);
1117 styleSymbols->appendComment(String8(ident), comment, srcPos);
1118 } else {
1119 symbols = NULL;
1120 }
1121
1122 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1123 if (code == ResXMLTree::START_TAG) {
1124 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
1125 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1126 && code != ResXMLTree::BAD_DOCUMENT) {
1127 if (code == ResXMLTree::END_TAG) {
1128 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
1129 break;
1130 }
1131 }
1132 }
1133 continue;
1134 } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
1135 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1136 && code != ResXMLTree::BAD_DOCUMENT) {
1137 if (code == ResXMLTree::END_TAG) {
1138 if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
1139 break;
1140 }
1141 }
1142 }
1143 continue;
1144 } else if (strcmp16(block.getElementName(&len), attr16.string()) != 0) {
1145 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1146 "Tag <%s> can not appear inside <declare-styleable>, only <attr>\n",
1147 String8(block.getElementName(&len)).string());
1148 return UNKNOWN_ERROR;
1149 }
1150
1151 String16 comment(
1152 block.getComment(&len) ? block.getComment(&len) : nulStr);
1153 String16 itemIdent;
1154 err = compileAttribute(in, block, myPackage, outTable, &itemIdent, true);
1155 if (err != NO_ERROR) {
1156 hasErrors = localHasErrors = true;
1157 }
1158
1159 if (symbols != NULL) {
1160 SourcePos srcPos(String8(in->getPrintableSource()), block.getLineNumber());
1161 symbols->addSymbol(String8(itemIdent), 0, srcPos);
1162 symbols->appendComment(String8(itemIdent), comment, srcPos);
1163 //printf("Attribute %s comment: %s\n", String8(itemIdent).string(),
1164 // String8(comment).string());
1165 }
1166 } else if (code == ResXMLTree::END_TAG) {
1167 if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
1168 break;
1169 }
1170
1171 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1172 "Found tag </%s> where </attr> is expected\n",
1173 String8(block.getElementName(&len)).string());
1174 return UNKNOWN_ERROR;
1175 }
1176 }
1177 continue;
1178
1179 } else if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
1180 err = compileAttribute(in, block, myPackage, outTable, NULL);
1181 if (err != NO_ERROR) {
1182 hasErrors = true;
1183 }
1184 continue;
1185
1186 } else if (strcmp16(block.getElementName(&len), item16.string()) == 0) {
1187 curTag = &item16;
1188 ssize_t attri = block.indexOfAttribute(NULL, "type");
1189 if (attri >= 0) {
1190 curType = String16(block.getAttributeStringValue(attri, &len));
1191 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
1192 if (formatIdx >= 0) {
1193 String16 formatStr = String16(block.getAttributeStringValue(
1194 formatIdx, &len));
1195 curFormat = parse_flags(formatStr.string(), formatStr.size(),
1196 gFormatFlags);
1197 if (curFormat == 0) {
1198 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1199 "Tag <item> 'format' attribute value \"%s\" not valid\n",
1200 String8(formatStr).string());
1201 hasErrors = localHasErrors = true;
1202 }
1203 }
1204 } else {
1205 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1206 "A 'type' attribute is required for <item>\n");
1207 hasErrors = localHasErrors = true;
1208 }
1209 curIsStyled = true;
1210 } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
1211 // Note the existence and locale of every string we process
1212 char rawLocale[16];
1213 curParams.getLocale(rawLocale);
1214 String8 locale(rawLocale);
1215 String16 name;
1216 String16 translatable;
6b12183e 1217 String16 formatted;
a534180c
TAOSP
1218
1219 size_t n = block.getAttributeCount();
1220 for (size_t i = 0; i < n; i++) {
1221 size_t length;
1222 const uint16_t* attr = block.getAttributeName(i, &length);
1223 if (strcmp16(attr, name16.string()) == 0) {
1224 name.setTo(block.getAttributeStringValue(i, &length));
1225 } else if (strcmp16(attr, translatable16.string()) == 0) {
1226 translatable.setTo(block.getAttributeStringValue(i, &length));
6b12183e
KR
1227 } else if (strcmp16(attr, formatted16.string()) == 0) {
1228 formatted.setTo(block.getAttributeStringValue(i, &length));
a534180c
TAOSP
1229 }
1230 }
1231
1232 if (name.size() > 0) {
1233 if (translatable == false16) {
6b12183e 1234 curIsFormatted = false;
a534180c
TAOSP
1235 // Untranslatable strings must only exist in the default [empty] locale
1236 if (locale.size() > 0) {
1237 fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
1238 " in locale '%s'\n", String8(name).string(),
1239 bundle->getResourceSourceDirs()[0],
1240 locale.string());
1241 // hasErrors = localHasErrors = true;
1242 } else {
1243 // Intentionally empty block:
1244 //
1245 // Don't add untranslatable strings to the localization table; that
1246 // way if we later see localizations of them, they'll be flagged as
1247 // having no default translation.
1248 }
1249 } else {
1250 outTable->addLocalization(name, locale);
1251 }
6b12183e
KR
1252
1253 if (formatted == false16) {
1254 curIsFormatted = false;
1255 }
a534180c
TAOSP
1256 }
1257
1258 curTag = &string16;
1259 curType = string16;
1260 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
1261 curIsStyled = true;
1262 curIsPseudolocalizable = true;
1263 } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) {
1264 curTag = &drawable16;
1265 curType = drawable16;
1266 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
1267 } else if (strcmp16(block.getElementName(&len), color16.string()) == 0) {
1268 curTag = &color16;
1269 curType = color16;
1270 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
1271 } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
1272 curTag = &bool16;
1273 curType = bool16;
1274 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
1275 } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
1276 curTag = &integer16;
1277 curType = integer16;
1278 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
1279 } else if (strcmp16(block.getElementName(&len), dimen16.string()) == 0) {
1280 curTag = &dimen16;
1281 curType = dimen16;
1282 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
1283 } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
1284 curTag = &fraction16;
1285 curType = fraction16;
1286 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
1287 } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
1288 curTag = &bag16;
1289 curIsBag = true;
1290 ssize_t attri = block.indexOfAttribute(NULL, "type");
1291 if (attri >= 0) {
1292 curType = String16(block.getAttributeStringValue(attri, &len));
1293 } else {
1294 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1295 "A 'type' attribute is required for <bag>\n");
1296 hasErrors = localHasErrors = true;
1297 }
1298 } else if (strcmp16(block.getElementName(&len), style16.string()) == 0) {
1299 curTag = &style16;
1300 curType = style16;
1301 curIsBag = true;
1302 } else if (strcmp16(block.getElementName(&len), plurals16.string()) == 0) {
1303 curTag = &plurals16;
1304 curType = plurals16;
1305 curIsBag = true;
1306 } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) {
1307 curTag = &array16;
1308 curType = array16;
1309 curIsBag = true;
eca7446f 1310 curIsBagReplaceOnOverwrite = true;
a534180c
TAOSP
1311 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
1312 if (formatIdx >= 0) {
1313 String16 formatStr = String16(block.getAttributeStringValue(
1314 formatIdx, &len));
1315 curFormat = parse_flags(formatStr.string(), formatStr.size(),
1316 gFormatFlags);
1317 if (curFormat == 0) {
1318 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1319 "Tag <array> 'format' attribute value \"%s\" not valid\n",
1320 String8(formatStr).string());
1321 hasErrors = localHasErrors = true;
1322 }
1323 }
1324 } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
444fcde5
JS
1325 // Check whether these strings need valid formats.
1326 // (simplified form of what string16 does above)
1327 size_t n = block.getAttributeCount();
1328 for (size_t i = 0; i < n; i++) {
1329 size_t length;
1330 const uint16_t* attr = block.getAttributeName(i, &length);
1331 if (strcmp16(attr, translatable16.string()) == 0
1332 || strcmp16(attr, formatted16.string()) == 0) {
1333 const uint16_t* value = block.getAttributeStringValue(i, &length);
1334 if (strcmp16(value, false16.string()) == 0) {
1335 curIsFormatted = false;
1336 break;
1337 }
1338 }
1339 }
1340
a534180c
TAOSP
1341 curTag = &string_array16;
1342 curType = array16;
1343 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
1344 curIsBag = true;
eca7446f 1345 curIsBagReplaceOnOverwrite = true;
a534180c
TAOSP
1346 curIsPseudolocalizable = true;
1347 } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
1348 curTag = &integer_array16;
1349 curType = array16;
1350 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
1351 curIsBag = true;
eca7446f 1352 curIsBagReplaceOnOverwrite = true;
a534180c
TAOSP
1353 } else {
1354 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1355 "Found tag %s where item is expected\n",
1356 String8(block.getElementName(&len)).string());
1357 return UNKNOWN_ERROR;
1358 }
1359
1360 String16 ident;
1361 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
1362 if (identIdx >= 0) {
1363 ident = String16(block.getAttributeStringValue(identIdx, &len));
1364 } else {
1365 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1366 "A 'name' attribute is required for <%s>\n",
1367 String8(*curTag).string());
1368 hasErrors = localHasErrors = true;
1369 }
1370
dcf97f4c
DH
1371 String16 product;
1372 identIdx = block.indexOfAttribute(NULL, "product");
1373 if (identIdx >= 0) {
1374 product = String16(block.getAttributeStringValue(identIdx, &len));
1375 }
1376
a534180c
TAOSP
1377 String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr);
1378
1379 if (curIsBag) {
1380 // Figure out the parent of this bag...
1381 String16 parentIdent;
1382 ssize_t parentIdentIdx = block.indexOfAttribute(NULL, "parent");
1383 if (parentIdentIdx >= 0) {
1384 parentIdent = String16(block.getAttributeStringValue(parentIdentIdx, &len));
1385 } else {
1386 ssize_t sep = ident.findLast('.');
1387 if (sep >= 0) {
1388 parentIdent.setTo(ident, sep);
1389 }
1390 }
1391
1392 if (!localHasErrors) {
eca7446f
RG
1393 err = outTable->startBag(SourcePos(in->getPrintableSource(),
1394 block.getLineNumber()), myPackage, curType, ident,
1395 parentIdent, &curParams,
1396 overwrite, curIsBagReplaceOnOverwrite);
a534180c
TAOSP
1397 if (err != NO_ERROR) {
1398 hasErrors = localHasErrors = true;
1399 }
1400 }
1401
1402 ssize_t elmIndex = 0;
1403 char elmIndexStr[14];
1404 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1405 && code != ResXMLTree::BAD_DOCUMENT) {
1406
1407 if (code == ResXMLTree::START_TAG) {
1408 if (strcmp16(block.getElementName(&len), item16.string()) != 0) {
1409 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1410 "Tag <%s> can not appear inside <%s>, only <item>\n",
1411 String8(block.getElementName(&len)).string(),
1412 String8(*curTag).string());
1413 return UNKNOWN_ERROR;
1414 }
1415
1416 String16 itemIdent;
1417 if (curType == array16) {
1418 sprintf(elmIndexStr, "^index_%d", (int)elmIndex++);
1419 itemIdent = String16(elmIndexStr);
1420 } else if (curType == plurals16) {
1421 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "quantity");
1422 if (itemIdentIdx >= 0) {
1423 String16 quantity16(block.getAttributeStringValue(itemIdentIdx, &len));
1424 if (quantity16 == other16) {
1425 itemIdent = quantityOther16;
1426 }
1427 else if (quantity16 == zero16) {
1428 itemIdent = quantityZero16;
1429 }
1430 else if (quantity16 == one16) {
1431 itemIdent = quantityOne16;
1432 }
1433 else if (quantity16 == two16) {
1434 itemIdent = quantityTwo16;
1435 }
1436 else if (quantity16 == few16) {
1437 itemIdent = quantityFew16;
1438 }
1439 else if (quantity16 == many16) {
1440 itemIdent = quantityMany16;
1441 }
1442 else {
1443 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1444 "Illegal 'quantity' attribute is <item> inside <plurals>\n");
1445 hasErrors = localHasErrors = true;
1446 }
1447 } else {
1448 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1449 "A 'quantity' attribute is required for <item> inside <plurals>\n");
1450 hasErrors = localHasErrors = true;
1451 }
1452 } else {
1453 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
1454 if (itemIdentIdx >= 0) {
1455 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
1456 } else {
1457 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1458 "A 'name' attribute is required for <item>\n");
1459 hasErrors = localHasErrors = true;
1460 }
1461 }
1462
1463 ResXMLParser::ResXMLPosition parserPosition;
1464 block.getPosition(&parserPosition);
1465
1466 err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
6b12183e 1467 ident, parentIdent, itemIdent, curFormat, curIsFormatted,
dcf97f4c 1468 product, false, overwrite, outTable);
a534180c
TAOSP
1469 if (err == NO_ERROR) {
1470 if (curIsPseudolocalizable && localeIsDefined(curParams)
1471 && bundle->getPseudolocalize()) {
1472 // pseudolocalize here
1473#if 1
1474 block.setPosition(parserPosition);
1475 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
6b12183e 1476 curType, ident, parentIdent, itemIdent, curFormat,
dcf97f4c 1477 curIsFormatted, product, true, overwrite, outTable);
a534180c
TAOSP
1478#endif
1479 }
1480 }
1481 if (err != NO_ERROR) {
1482 hasErrors = localHasErrors = true;
1483 }
1484 } else if (code == ResXMLTree::END_TAG) {
1485 if (strcmp16(block.getElementName(&len), curTag->string()) != 0) {
1486 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1487 "Found tag </%s> where </%s> is expected\n",
1488 String8(block.getElementName(&len)).string(),
1489 String8(*curTag).string());
1490 return UNKNOWN_ERROR;
1491 }
1492 break;
1493 }
1494 }
1495 } else {
1496 ResXMLParser::ResXMLPosition parserPosition;
1497 block.getPosition(&parserPosition);
1498
1499 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
6b12183e 1500 *curTag, curIsStyled, curFormat, curIsFormatted,
dcf97f4c 1501 product, false, overwrite, outTable);
a534180c
TAOSP
1502
1503 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
1504 hasErrors = localHasErrors = true;
1505 }
1506 else if (err == NO_ERROR) {
1507 if (curIsPseudolocalizable && localeIsDefined(curParams)
1508 && bundle->getPseudolocalize()) {
1509 // pseudolocalize here
1510 block.setPosition(parserPosition);
1511 err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
6b12183e 1512 ident, *curTag, curIsStyled, curFormat,
dcf97f4c 1513 curIsFormatted, product,
b8ea3a3f 1514 true, overwrite, outTable);
a534180c
TAOSP
1515 if (err != NO_ERROR) {
1516 hasErrors = localHasErrors = true;
1517 }
1518 }
1519 }
1520 }
1521
1522#if 0
1523 if (comment.size() > 0) {
1524 printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).string(),
1525 String8(curType).string(), String8(ident).string(),
1526 String8(comment).string());
1527 }
1528#endif
1529 if (!localHasErrors) {
1530 outTable->appendComment(myPackage, curType, ident, comment, false);
1531 }
1532 }
1533 else if (code == ResXMLTree::END_TAG) {
1534 if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
1535 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1536 "Unexpected end tag %s\n", String8(block.getElementName(&len)).string());
1537 return UNKNOWN_ERROR;
1538 }
1539 }
1540 else if (code == ResXMLTree::START_NAMESPACE || code == ResXMLTree::END_NAMESPACE) {
1541 }
1542 else if (code == ResXMLTree::TEXT) {
1543 if (isWhitespace(block.getText(&len))) {
1544 continue;
1545 }
1546 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1547 "Found text \"%s\" where item tag is expected\n",
1548 String8(block.getText(&len)).string());
1549 return UNKNOWN_ERROR;
1550 }
1551 }
1552
1553 return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
1554}
1555
1556ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage)
1557 : mAssetsPackage(assetsPackage), mNextPackageId(1), mHaveAppPackage(false),
1558 mIsAppPackage(!bundle->getExtending()),
1559 mNumLocal(0),
1560 mBundle(bundle)
1561{
1562}
1563
1564status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets)
1565{
1566 status_t err = assets->buildIncludedResources(bundle);
1567 if (err != NO_ERROR) {
1568 return err;
1569 }
1570
1571 // For future reference to included resources.
1572 mAssets = assets;
1573
1574 const ResTable& incl = assets->getIncludedResources();
1575
1576 // Retrieve all the packages.
1577 const size_t N = incl.getBasePackageCount();
1578 for (size_t phase=0; phase<2; phase++) {
1579 for (size_t i=0; i<N; i++) {
1580 String16 name(incl.getBasePackageName(i));
1581 uint32_t id = incl.getBasePackageId(i);
1582 // First time through: only add base packages (id
1583 // is not 0); second time through add the other
1584 // packages.
1585 if (phase != 0) {
1586 if (id != 0) {
1587 // Skip base packages -- already one.
1588 id = 0;
1589 } else {
1590 // Assign a dynamic id.
1591 id = mNextPackageId;
1592 }
1593 } else if (id != 0) {
1594 if (id == 127) {
1595 if (mHaveAppPackage) {
7e486e33 1596 fprintf(stderr, "Included resources have two application packages!\n");
a534180c
TAOSP
1597 return UNKNOWN_ERROR;
1598 }
1599 mHaveAppPackage = true;
1600 }
1601 if (mNextPackageId > id) {
1602 fprintf(stderr, "Included base package ID %d already in use!\n", id);
1603 return UNKNOWN_ERROR;
1604 }
1605 }
1606 if (id != 0) {
1607 NOISY(printf("Including package %s with ID=%d\n",
1608 String8(name).string(), id));
1609 sp<Package> p = new Package(name, id);
1610 mPackages.add(name, p);
1611 mOrderedPackages.add(p);
1612
1613 if (id >= mNextPackageId) {
1614 mNextPackageId = id+1;
1615 }
1616 }
1617 }
1618 }
1619
1620 // Every resource table always has one first entry, the bag attributes.
1621 const SourcePos unknown(String8("????"), 0);
1622 sp<Type> attr = getType(mAssetsPackage, String16("attr"), unknown);
1623
1624 return NO_ERROR;
1625}
1626
1627status_t ResourceTable::addPublic(const SourcePos& sourcePos,
1628 const String16& package,
1629 const String16& type,
1630 const String16& name,
1631 const uint32_t ident)
1632{
1633 uint32_t rid = mAssets->getIncludedResources()
1634 .identifierForName(name.string(), name.size(),
1635 type.string(), type.size(),
1636 package.string(), package.size());
1637 if (rid != 0) {
1638 sourcePos.error("Error declaring public resource %s/%s for included package %s\n",
1639 String8(type).string(), String8(name).string(),
1640 String8(package).string());
1641 return UNKNOWN_ERROR;
1642 }
1643
1644 sp<Type> t = getType(package, type, sourcePos);
1645 if (t == NULL) {
1646 return UNKNOWN_ERROR;
1647 }
1648 return t->addPublic(sourcePos, name, ident);
1649}
1650
1651status_t ResourceTable::addEntry(const SourcePos& sourcePos,
1652 const String16& package,
1653 const String16& type,
1654 const String16& name,
1655 const String16& value,
1656 const Vector<StringPool::entry_style_span>* style,
1657 const ResTable_config* params,
1658 const bool doSetIndex,
1659 const int32_t format,
1660 const bool overwrite)
1661{
1662 // Check for adding entries in other packages... for now we do
1663 // nothing. We need to do the right thing here to support skinning.
1664 uint32_t rid = mAssets->getIncludedResources()
1665 .identifierForName(name.string(), name.size(),
1666 type.string(), type.size(),
1667 package.string(), package.size());
1668 if (rid != 0) {
1669 return NO_ERROR;
1670 }
1671
1672#if 0
1673 if (name == String16("left")) {
1674 printf("Adding entry left: file=%s, line=%d, type=%s, value=%s\n",
1675 sourcePos.file.string(), sourcePos.line, String8(type).string(),
1676 String8(value).string());
1677 }
1678#endif
6c9f72e4
RG
1679
1680 sp<Entry> e = getEntry(package, type, name, sourcePos, overwrite,
1681 params, doSetIndex);
a534180c
TAOSP
1682 if (e == NULL) {
1683 return UNKNOWN_ERROR;
1684 }
1685 status_t err = e->setItem(sourcePos, value, style, format, overwrite);
1686 if (err == NO_ERROR) {
1687 mNumLocal++;
1688 }
1689 return err;
1690}
1691
1692status_t ResourceTable::startBag(const SourcePos& sourcePos,
1693 const String16& package,
1694 const String16& type,
1695 const String16& name,
1696 const String16& bagParent,
1697 const ResTable_config* params,
eca7446f 1698 bool overlay,
a534180c
TAOSP
1699 bool replace, bool isId)
1700{
76486813
RG
1701 status_t result = NO_ERROR;
1702
a534180c
TAOSP
1703 // Check for adding entries in other packages... for now we do
1704 // nothing. We need to do the right thing here to support skinning.
1705 uint32_t rid = mAssets->getIncludedResources()
1706 .identifierForName(name.string(), name.size(),
1707 type.string(), type.size(),
1708 package.string(), package.size());
1709 if (rid != 0) {
1710 return NO_ERROR;
1711 }
1712
1713#if 0
1714 if (name == String16("left")) {
1715 printf("Adding bag left: file=%s, line=%d, type=%s\n",
1716 sourcePos.file.striing(), sourcePos.line, String8(type).string());
1717 }
1718#endif
636d3b70 1719 if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) {
a8c9cd56
DH
1720 bool canAdd = false;
1721 sp<Package> p = mPackages.valueFor(package);
1722 if (p != NULL) {
1723 sp<Type> t = p->getTypes().valueFor(type);
1724 if (t != NULL) {
1725 if (t->getCanAddEntries().indexOf(name) >= 0) {
1726 canAdd = true;
1727 }
1728 }
1729 }
1730 if (!canAdd) {
1731 sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n",
1732 String8(name).string());
1733 return UNKNOWN_ERROR;
1734 }
eca7446f 1735 }
6c9f72e4 1736 sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params);
a534180c
TAOSP
1737 if (e == NULL) {
1738 return UNKNOWN_ERROR;
1739 }
1740
1741 // If a parent is explicitly specified, set it.
1742 if (bagParent.size() > 0) {
a534180c
TAOSP
1743 e->setParent(bagParent);
1744 }
76486813
RG
1745
1746 if ((result = e->makeItABag(sourcePos)) != NO_ERROR) {
1747 return result;
1748 }
9d911eb6 1749
eca7446f 1750 if (overlay && replace) {
9d911eb6
RG
1751 return e->emptyBag(sourcePos);
1752 }
1753 return result;
a534180c
TAOSP
1754}
1755
1756status_t ResourceTable::addBag(const SourcePos& sourcePos,
1757 const String16& package,
1758 const String16& type,
1759 const String16& name,
1760 const String16& bagParent,
1761 const String16& bagKey,
1762 const String16& value,
1763 const Vector<StringPool::entry_style_span>* style,
1764 const ResTable_config* params,
1765 bool replace, bool isId, const int32_t format)
1766{
1767 // Check for adding entries in other packages... for now we do
1768 // nothing. We need to do the right thing here to support skinning.
1769 uint32_t rid = mAssets->getIncludedResources()
1770 .identifierForName(name.string(), name.size(),
1771 type.string(), type.size(),
1772 package.string(), package.size());
1773 if (rid != 0) {
1774 return NO_ERROR;
1775 }
1776
1777#if 0
1778 if (name == String16("left")) {
1779 printf("Adding bag left: file=%s, line=%d, type=%s\n",
1780 sourcePos.file.striing(), sourcePos.line, String8(type).string());
1781 }
1782#endif
6c9f72e4 1783 sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params);
a534180c
TAOSP
1784 if (e == NULL) {
1785 return UNKNOWN_ERROR;
1786 }
1787
1788 // If a parent is explicitly specified, set it.
1789 if (bagParent.size() > 0) {
a534180c
TAOSP
1790 e->setParent(bagParent);
1791 }
1792
1793 const bool first = e->getBag().indexOfKey(bagKey) < 0;
1794 status_t err = e->addToBag(sourcePos, bagKey, value, style, replace, isId, format);
1795 if (err == NO_ERROR && first) {
1796 mNumLocal++;
1797 }
1798 return err;
1799}
1800
1801bool ResourceTable::hasBagOrEntry(const String16& package,
1802 const String16& type,
1803 const String16& name) const
1804{
1805 // First look for this in the included resources...
1806 uint32_t rid = mAssets->getIncludedResources()
1807 .identifierForName(name.string(), name.size(),
1808 type.string(), type.size(),
1809 package.string(), package.size());
1810 if (rid != 0) {
1811 return true;
1812 }
1813
1814 sp<Package> p = mPackages.valueFor(package);
1815 if (p != NULL) {
1816 sp<Type> t = p->getTypes().valueFor(type);
1817 if (t != NULL) {
1818 sp<ConfigList> c = t->getConfigs().valueFor(name);
1819 if (c != NULL) return true;
1820 }
1821 }
1822
1823 return false;
1824}
1825
8a9f9549
EF
1826bool ResourceTable::hasBagOrEntry(const String16& package,
1827 const String16& type,
1828 const String16& name,
1829 const ResTable_config& config) const
1830{
1831 // First look for this in the included resources...
1832 uint32_t rid = mAssets->getIncludedResources()
1833 .identifierForName(name.string(), name.size(),
1834 type.string(), type.size(),
1835 package.string(), package.size());
1836 if (rid != 0) {
1837 return true;
1838 }
1839
1840 sp<Package> p = mPackages.valueFor(package);
1841 if (p != NULL) {
1842 sp<Type> t = p->getTypes().valueFor(type);
1843 if (t != NULL) {
1844 sp<ConfigList> c = t->getConfigs().valueFor(name);
1845 if (c != NULL) {
1846 sp<Entry> e = c->getEntries().valueFor(config);
1847 if (e != NULL) {
1848 return true;
1849 }
1850 }
1851 }
1852 }
1853
1854 return false;
1855}
1856
a534180c
TAOSP
1857bool ResourceTable::hasBagOrEntry(const String16& ref,
1858 const String16* defType,
1859 const String16* defPackage)
1860{
1861 String16 package, type, name;
1862 if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
1863 defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
1864 return false;
1865 }
1866 return hasBagOrEntry(package, type, name);
1867}
1868
1869bool ResourceTable::appendComment(const String16& package,
1870 const String16& type,
1871 const String16& name,
1872 const String16& comment,
1873 bool onlyIfEmpty)
1874{
1875 if (comment.size() <= 0) {
1876 return true;
1877 }
1878
1879 sp<Package> p = mPackages.valueFor(package);
1880 if (p != NULL) {
1881 sp<Type> t = p->getTypes().valueFor(type);
1882 if (t != NULL) {
1883 sp<ConfigList> c = t->getConfigs().valueFor(name);
1884 if (c != NULL) {
1885 c->appendComment(comment, onlyIfEmpty);
1886 return true;
1887 }
1888 }
1889 }
1890 return false;
1891}
1892
1893bool ResourceTable::appendTypeComment(const String16& package,
1894 const String16& type,
1895 const String16& name,
1896 const String16& comment)
1897{
1898 if (comment.size() <= 0) {
1899 return true;
1900 }
1901
1902 sp<Package> p = mPackages.valueFor(package);
1903 if (p != NULL) {
1904 sp<Type> t = p->getTypes().valueFor(type);
1905 if (t != NULL) {
1906 sp<ConfigList> c = t->getConfigs().valueFor(name);
1907 if (c != NULL) {
1908 c->appendTypeComment(comment);
1909 return true;
1910 }
1911 }
1912 }
1913 return false;
1914}
1915
a8c9cd56
DH
1916void ResourceTable::canAddEntry(const SourcePos& pos,
1917 const String16& package, const String16& type, const String16& name)
1918{
1919 sp<Type> t = getType(package, type, pos);
1920 if (t != NULL) {
1921 t->canAddEntry(name);
1922 }
1923}
1924
a534180c
TAOSP
1925size_t ResourceTable::size() const {
1926 return mPackages.size();
1927}
1928
1929size_t ResourceTable::numLocalResources() const {
1930 return mNumLocal;
1931}
1932
1933bool ResourceTable::hasResources() const {
1934 return mNumLocal > 0;
1935}
1936
1937sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
1938{
1939 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
1940 status_t err = flatten(bundle, data);
1941 return err == NO_ERROR ? data : NULL;
1942}
1943
1944inline uint32_t ResourceTable::getResId(const sp<Package>& p,
1945 const sp<Type>& t,
1946 uint32_t nameId)
1947{
1948 return makeResId(p->getAssignedId(), t->getIndex(), nameId);
1949}
1950
1951uint32_t ResourceTable::getResId(const String16& package,
1952 const String16& type,
1953 const String16& name,
1954 bool onlyPublic) const
1955{
1956 sp<Package> p = mPackages.valueFor(package);
1957 if (p == NULL) return 0;
1958
1959 // First look for this in the included resources...
1960 uint32_t specFlags = 0;
1961 uint32_t rid = mAssets->getIncludedResources()
1962 .identifierForName(name.string(), name.size(),
1963 type.string(), type.size(),
1964 package.string(), package.size(),
1965 &specFlags);
1966 if (rid != 0) {
1967 if (onlyPublic) {
1968 if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
1969 return 0;
1970 }
1971 }
1972
1973 if (Res_INTERNALID(rid)) {
1974 return rid;
1975 }
1976 return Res_MAKEID(p->getAssignedId()-1,
1977 Res_GETTYPE(rid),
1978 Res_GETENTRY(rid));
1979 }
1980
1981 sp<Type> t = p->getTypes().valueFor(type);
1982 if (t == NULL) return 0;
1983 sp<ConfigList> c = t->getConfigs().valueFor(name);
1984 if (c == NULL) return 0;
1985 int32_t ei = c->getEntryIndex();
1986 if (ei < 0) return 0;
1987 return getResId(p, t, ei);
1988}
1989
1990uint32_t ResourceTable::getResId(const String16& ref,
1991 const String16* defType,
1992 const String16* defPackage,
1993 const char** outErrorMsg,
1994 bool onlyPublic) const
1995{
1996 String16 package, type, name;
2b01c9f2 1997 bool refOnlyPublic = true;
a534180c
TAOSP
1998 if (!ResTable::expandResourceRef(
1999 ref.string(), ref.size(), &package, &type, &name,
2000 defType, defPackage ? defPackage:&mAssetsPackage,
2b01c9f2 2001 outErrorMsg, &refOnlyPublic)) {
a534180c
TAOSP
2002 NOISY(printf("Expanding resource: ref=%s\n",
2003 String8(ref).string()));
2004 NOISY(printf("Expanding resource: defType=%s\n",
2005 defType ? String8(*defType).string() : "NULL"));
2006 NOISY(printf("Expanding resource: defPackage=%s\n",
2007 defPackage ? String8(*defPackage).string() : "NULL"));
2008 NOISY(printf("Expanding resource: ref=%s\n", String8(ref).string()));
2009 NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
2010 String8(package).string(), String8(type).string(),
2011 String8(name).string()));
2012 return 0;
2013 }
2b01c9f2 2014 uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic);
a534180c
TAOSP
2015 NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
2016 String8(package).string(), String8(type).string(),
2017 String8(name).string(), res));
2018 if (res == 0) {
2019 if (outErrorMsg)
2020 *outErrorMsg = "No resource found that matches the given name";
2021 }
2022 return res;
2023}
2024
2025bool ResourceTable::isValidResourceName(const String16& s)
2026{
2027 const char16_t* p = s.string();
2028 bool first = true;
2029 while (*p) {
2030 if ((*p >= 'a' && *p <= 'z')
2031 || (*p >= 'A' && *p <= 'Z')
2032 || *p == '_'
2033 || (!first && *p >= '0' && *p <= '9')) {
2034 first = false;
2035 p++;
2036 continue;
2037 }
2038 return false;
2039 }
2040 return true;
2041}
2042
2043bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
2044 const String16& str,
2045 bool preserveSpaces, bool coerceType,
2046 uint32_t attrID,
2047 const Vector<StringPool::entry_style_span>* style,
2048 String16* outStr, void* accessorCookie,
2049 uint32_t attrType)
2050{
2051 String16 finalStr;
2052
2053 bool res = true;
2054 if (style == NULL || style->size() == 0) {
2055 // Text is not styled so it can be any type... let's figure it out.
2056 res = mAssets->getIncludedResources()
2057 .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
2058 coerceType, attrID, NULL, &mAssetsPackage, this,
2059 accessorCookie, attrType);
2060 } else {
2061 // Styled text can only be a string, and while collecting the style
2062 // information we have already processed that string!
2063 outValue->size = sizeof(Res_value);
2064 outValue->res0 = 0;
84069cb2 2065 outValue->dataType = outValue->TYPE_STRING;
a534180c
TAOSP
2066 outValue->data = 0;
2067 finalStr = str;
2068 }
2069
2070 if (!res) {
2071 return false;
2072 }
2073
84069cb2 2074 if (outValue->dataType == outValue->TYPE_STRING) {
a534180c
TAOSP
2075 // Should do better merging styles.
2076 if (pool) {
2077 if (style != NULL && style->size() > 0) {
2078 outValue->data = pool->add(finalStr, *style);
2079 } else {
2080 outValue->data = pool->add(finalStr, true);
2081 }
2082 } else {
2083 // Caller will fill this in later.
2084 outValue->data = 0;
2085 }
2086
2087 if (outStr) {
2088 *outStr = finalStr;
2089 }
2090
2091 }
2092
2093 return true;
2094}
2095
2096uint32_t ResourceTable::getCustomResource(
2097 const String16& package, const String16& type, const String16& name) const
2098{
2099 //printf("getCustomResource: %s %s %s\n", String8(package).string(),
2100 // String8(type).string(), String8(name).string());
2101 sp<Package> p = mPackages.valueFor(package);
2102 if (p == NULL) return 0;
2103 sp<Type> t = p->getTypes().valueFor(type);
2104 if (t == NULL) return 0;
2105 sp<ConfigList> c = t->getConfigs().valueFor(name);
2106 if (c == NULL) return 0;
2107 int32_t ei = c->getEntryIndex();
2108 if (ei < 0) return 0;
2109 return getResId(p, t, ei);
2110}
2111
2112uint32_t ResourceTable::getCustomResourceWithCreation(
2113 const String16& package, const String16& type, const String16& name,
2114 const bool createIfNotFound)
2115{
2116 uint32_t resId = getCustomResource(package, type, name);
2117 if (resId != 0 || !createIfNotFound) {
2118 return resId;
2119 }
2120 String16 value("false");
2121
2122 status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
2123 if (status == NO_ERROR) {
2124 resId = getResId(package, type, name);
2125 return resId;
2126 }
2127 return 0;
2128}
2129
2130uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
2131{
2132 return origPackage;
2133}
2134
2135bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
2136{
2137 //printf("getAttributeType #%08x\n", attrID);
2138 Res_value value;
2139 if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
2140 //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
2141 // String8(getEntry(attrID)->getName()).string(), value.data);
2142 *outType = value.data;
2143 return true;
2144 }
2145 return false;
2146}
2147
2148bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
2149{
2150 //printf("getAttributeMin #%08x\n", attrID);
2151 Res_value value;
2152 if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
2153 *outMin = value.data;
2154 return true;
2155 }
2156 return false;
2157}
2158
2159bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
2160{
2161 //printf("getAttributeMax #%08x\n", attrID);
2162 Res_value value;
2163 if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
2164 *outMax = value.data;
2165 return true;
2166 }
2167 return false;
2168}
2169
2170uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
2171{
2172 //printf("getAttributeL10N #%08x\n", attrID);
2173 Res_value value;
2174 if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
2175 return value.data;
2176 }
2177 return ResTable_map::L10N_NOT_REQUIRED;
2178}
2179
2180bool ResourceTable::getLocalizationSetting()
2181{
2182 return mBundle->getRequireLocalization();
2183}
2184
2185void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
2186{
2187 if (accessorCookie != NULL && fmt != NULL) {
2188 AccessorCookie* ac = (AccessorCookie*)accessorCookie;
2189 int retval=0;
2190 char buf[1024];
2191 va_list ap;
2192 va_start(ap, fmt);
2193 retval = vsnprintf(buf, sizeof(buf), fmt, ap);
2194 va_end(ap);
2195 ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
2196 buf, ac->attr.string(), ac->value.string());
2197 }
2198}
2199
2200bool ResourceTable::getAttributeKeys(
2201 uint32_t attrID, Vector<String16>* outKeys)
2202{
2203 sp<const Entry> e = getEntry(attrID);
2204 if (e != NULL) {
2205 const size_t N = e->getBag().size();
2206 for (size_t i=0; i<N; i++) {
2207 const String16& key = e->getBag().keyAt(i);
2208 if (key.size() > 0 && key.string()[0] != '^') {
2209 outKeys->add(key);
2210 }
2211 }
2212 return true;
2213 }
2214 return false;
2215}
2216
2217bool ResourceTable::getAttributeEnum(
2218 uint32_t attrID, const char16_t* name, size_t nameLen,
2219 Res_value* outValue)
2220{
2221 //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
2222 String16 nameStr(name, nameLen);
2223 sp<const Entry> e = getEntry(attrID);
2224 if (e != NULL) {
2225 const size_t N = e->getBag().size();
2226 for (size_t i=0; i<N; i++) {
2227 //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
2228 // String8(e->getBag().keyAt(i)).string());
2229 if (e->getBag().keyAt(i) == nameStr) {
2230 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
2231 }
2232 }
2233 }
2234 return false;
2235}
2236
2237bool ResourceTable::getAttributeFlags(
2238 uint32_t attrID, const char16_t* name, size_t nameLen,
2239 Res_value* outValue)
2240{
2241 outValue->dataType = Res_value::TYPE_INT_HEX;
2242 outValue->data = 0;
2243
2244 //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
2245 String16 nameStr(name, nameLen);
2246 sp<const Entry> e = getEntry(attrID);
2247 if (e != NULL) {
2248 const size_t N = e->getBag().size();
2249
2250 const char16_t* end = name + nameLen;
2251 const char16_t* pos = name;
2252 bool failed = false;
2253 while (pos < end && !failed) {
2254 const char16_t* start = pos;
2255 end++;
2256 while (pos < end && *pos != '|') {
2257 pos++;
2258 }
2259
2260 String16 nameStr(start, pos-start);
2261 size_t i;
2262 for (i=0; i<N; i++) {
2263 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
2264 // String8(e->getBag().keyAt(i)).string());
2265 if (e->getBag().keyAt(i) == nameStr) {
2266 Res_value val;
2267 bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
2268 if (!got) {
2269 return false;
2270 }
2271 //printf("Got value: 0x%08x\n", val.data);
2272 outValue->data |= val.data;
2273 break;
2274 }
2275 }
2276
2277 if (i >= N) {
2278 // Didn't find this flag identifier.
2279 return false;
2280 }
2281 if (pos < end) {
2282 pos++;
2283 }
2284 }
2285
2286 return true;
2287 }
2288 return false;
2289}
2290
2291status_t ResourceTable::assignResourceIds()
2292{
2293 const size_t N = mOrderedPackages.size();
2294 size_t pi;
2295 status_t firstError = NO_ERROR;
2296
2297 // First generate all bag attributes and assign indices.
2298 for (pi=0; pi<N; pi++) {
2299 sp<Package> p = mOrderedPackages.itemAt(pi);
2300 if (p == NULL || p->getTypes().size() == 0) {
2301 // Empty, skip!
2302 continue;
2303 }
2304
2305 status_t err = p->applyPublicTypeOrder();
2306 if (err != NO_ERROR && firstError == NO_ERROR) {
2307 firstError = err;
2308 }
2309
2310 // Generate attributes...
2311 const size_t N = p->getOrderedTypes().size();
2312 size_t ti;
2313 for (ti=0; ti<N; ti++) {
2314 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2315 if (t == NULL) {
2316 continue;
2317 }
2318 const size_t N = t->getOrderedConfigs().size();
2319 for (size_t ci=0; ci<N; ci++) {
2320 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2321 if (c == NULL) {
2322 continue;
2323 }
2324 const size_t N = c->getEntries().size();
2325 for (size_t ei=0; ei<N; ei++) {
2326 sp<Entry> e = c->getEntries().valueAt(ei);
2327 if (e == NULL) {
2328 continue;
2329 }
2330 status_t err = e->generateAttributes(this, p->getName());
2331 if (err != NO_ERROR && firstError == NO_ERROR) {
2332 firstError = err;
2333 }
2334 }
2335 }
2336 }
2337
2338 const SourcePos unknown(String8("????"), 0);
2339 sp<Type> attr = p->getType(String16("attr"), unknown);
2340
2341 // Assign indices...
2342 for (ti=0; ti<N; ti++) {
2343 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2344 if (t == NULL) {
2345 continue;
2346 }
2347 err = t->applyPublicEntryOrder();
2348 if (err != NO_ERROR && firstError == NO_ERROR) {
2349 firstError = err;
2350 }
2351
2352 const size_t N = t->getOrderedConfigs().size();
2353 t->setIndex(ti+1);
2354
2355 LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
2356 "First type is not attr!");
2357
2358 for (size_t ei=0; ei<N; ei++) {
2359 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
2360 if (c == NULL) {
2361 continue;
2362 }
2363 c->setEntryIndex(ei);
2364 }
2365 }
2366
2367 // Assign resource IDs to keys in bags...
2368 for (ti=0; ti<N; ti++) {
2369 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2370 if (t == NULL) {
2371 continue;
2372 }
2373 const size_t N = t->getOrderedConfigs().size();
2374 for (size_t ci=0; ci<N; ci++) {
2375 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2376 //printf("Ordered config #%d: %p\n", ci, c.get());
2377 const size_t N = c->getEntries().size();
2378 for (size_t ei=0; ei<N; ei++) {
2379 sp<Entry> e = c->getEntries().valueAt(ei);
2380 if (e == NULL) {
2381 continue;
2382 }
2383 status_t err = e->assignResourceIds(this, p->getName());
2384 if (err != NO_ERROR && firstError == NO_ERROR) {
2385 firstError = err;
2386 }
2387 }
2388 }
2389 }
2390 }
2391 return firstError;
2392}
2393
2394status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
2395 const size_t N = mOrderedPackages.size();
2396 size_t pi;
2397
2398 for (pi=0; pi<N; pi++) {
2399 sp<Package> p = mOrderedPackages.itemAt(pi);
2400 if (p->getTypes().size() == 0) {
2401 // Empty, skip!
2402 continue;
2403 }
2404
2405 const size_t N = p->getOrderedTypes().size();
2406 size_t ti;
2407
2408 for (ti=0; ti<N; ti++) {
2409 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2410 if (t == NULL) {
2411 continue;
2412 }
2413 const size_t N = t->getOrderedConfigs().size();
2414 sp<AaptSymbols> typeSymbols;
2415 typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
2416 for (size_t ci=0; ci<N; ci++) {
2417 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2418 if (c == NULL) {
2419 continue;
2420 }
2421 uint32_t rid = getResId(p, t, ci);
2422 if (rid == 0) {
2423 return UNKNOWN_ERROR;
2424 }
2425 if (Res_GETPACKAGE(rid) == (size_t)(p->getAssignedId()-1)) {
2426 typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
2427
2428 String16 comment(c->getComment());
2429 typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
2430 //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(),
2431 // String8(comment).string());
2432 comment = c->getTypeComment();
2433 typeSymbols->appendTypeComment(String8(c->getName()), comment);
2434 } else {
2435#if 0
2436 printf("**** NO MATCH: 0x%08x vs 0x%08x\n",
2437 Res_GETPACKAGE(rid), p->getAssignedId());
2438#endif
2439 }
2440 }
2441 }
2442 }
2443 return NO_ERROR;
2444}
2445
2446
2447void
2448ResourceTable::addLocalization(const String16& name, const String8& locale)
2449{
2450 mLocalizations[name].insert(locale);
2451}
2452
2453
2454/*!
2455 * Flag various sorts of localization problems. '+' indicates checks already implemented;
2456 * '-' indicates checks that will be implemented in the future.
2457 *
2458 * + A localized string for which no default-locale version exists => warning
2459 * + A string for which no version in an explicitly-requested locale exists => warning
2460 * + A localized translation of an translateable="false" string => warning
2461 * - A localized string not provided in every locale used by the table
2462 */
2463status_t
2464ResourceTable::validateLocalizations(void)
2465{
2466 status_t err = NO_ERROR;
2467 const String8 defaultLocale;
2468
2469 // For all strings...
2470 for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
2471 nameIter != mLocalizations.end();
2472 nameIter++) {
2473 const set<String8>& configSet = nameIter->second; // naming convenience
2474
2475 // Look for strings with no default localization
2476 if (configSet.count(defaultLocale) == 0) {
2477 fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
2478 String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
c70f9935 2479 for (set<String8>::const_iterator locales = configSet.begin();
a534180c
TAOSP
2480 locales != configSet.end();
2481 locales++) {
2482 fprintf(stdout, " %s", (*locales).string());
2483 }
2484 fprintf(stdout, "\n");
2485 // !!! TODO: throw an error here in some circumstances
2486 }
2487
2488 // Check that all requested localizations are present for this string
2489 if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
2490 const char* allConfigs = mBundle->getConfigurations();
2491 const char* start = allConfigs;
2492 const char* comma;
2493
2494 do {
2495 String8 config;
2496 comma = strchr(start, ',');
2497 if (comma != NULL) {
2498 config.setTo(start, comma - start);
2499 start = comma + 1;
2500 } else {
2501 config.setTo(start);
2502 }
2503
2504 // don't bother with the pseudolocale "zz_ZZ"
2505 if (config != "zz_ZZ") {
2506 if (configSet.find(config) == configSet.end()) {
2507 // okay, no specific localization found. it's possible that we are
2508 // requiring a specific regional localization [e.g. de_DE] but there is an
2509 // available string in the generic language localization [e.g. de];
2510 // consider that string to have fulfilled the localization requirement.
2511 String8 region(config.string(), 2);
2512 if (configSet.find(region) == configSet.end()) {
2513 if (configSet.count(defaultLocale) == 0) {
1782ef54 2514 fprintf(stdout, "aapt: warning: "
571313a5 2515 "**** string '%s' has no default or required localization "
a534180c
TAOSP
2516 "for '%s' in %s\n",
2517 String8(nameIter->first).string(),
2518 config.string(),
2519 mBundle->getResourceSourceDirs()[0]);
a534180c
TAOSP
2520 }
2521 }
2522 }
2523 }
2524 } while (comma != NULL);
2525 }
2526 }
2527
2528 return err;
2529}
2530
2531
2532status_t
2533ResourceFilter::parse(const char* arg)
2534{
2535 if (arg == NULL) {
2536 return 0;
2537 }
2538
2539 const char* p = arg;
2540 const char* q;
2541
2542 while (true) {
2543 q = strchr(p, ',');
2544 if (q == NULL) {
2545 q = p + strlen(p);
2546 }
2547
2548 String8 part(p, q-p);
2549
2550 if (part == "zz_ZZ") {
2551 mContainsPseudo = true;
2552 }
2553 int axis;
2554 uint32_t value;
2555 if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
2556 fprintf(stderr, "Invalid configuration: %s\n", arg);
2557 fprintf(stderr, " ");
2558 for (int i=0; i<p-arg; i++) {
2559 fprintf(stderr, " ");
2560 }
2561 for (int i=0; i<q-p; i++) {
2562 fprintf(stderr, "^");
2563 }
2564 fprintf(stderr, "\n");
2565 return 1;
2566 }
2567
2568 ssize_t index = mData.indexOfKey(axis);
2569 if (index < 0) {
2570 mData.add(axis, SortedVector<uint32_t>());
2571 }
2572 SortedVector<uint32_t>& sv = mData.editValueFor(axis);
2573 sv.add(value);
2574 // if it's a locale with a region, also match an unmodified locale of the
2575 // same language
2576 if (axis == AXIS_LANGUAGE) {
2577 if (value & 0xffff0000) {
2578 sv.add(value & 0x0000ffff);
2579 }
2580 }
2581 p = q;
2582 if (!*p) break;
2583 p++;
2584 }
2585
2586 return NO_ERROR;
2587}
2588
2589bool
460ed1b2 2590ResourceFilter::match(int axis, uint32_t value) const
a534180c
TAOSP
2591{
2592 if (value == 0) {
2593 // they didn't specify anything so take everything
2594 return true;
2595 }
2596 ssize_t index = mData.indexOfKey(axis);
2597 if (index < 0) {
2598 // we didn't request anything on this axis so take everything
2599 return true;
2600 }
2601 const SortedVector<uint32_t>& sv = mData.valueAt(index);
2602 return sv.indexOf(value) >= 0;
2603}
2604
2605bool
460ed1b2 2606ResourceFilter::match(const ResTable_config& config) const
a534180c
TAOSP
2607{
2608 if (config.locale) {
2609 uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
2610 | (config.language[1] << 8) | (config.language[0]);
2611 if (!match(AXIS_LANGUAGE, locale)) {
2612 return false;
2613 }
2614 }
2615 if (!match(AXIS_ORIENTATION, config.orientation)) {
2616 return false;
2617 }
2ca01a37
TH
2618 if (!match(AXIS_UIMODETYPE, (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE))) {
2619 return false;
2620 }
2621 if (!match(AXIS_UIMODENIGHT, (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT))) {
2622 return false;
2623 }
a534180c
TAOSP
2624 if (!match(AXIS_DENSITY, config.density)) {
2625 return false;
2626 }
2627 if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
2628 return false;
2629 }
2630 if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
2631 return false;
2632 }
2633 if (!match(AXIS_KEYBOARD, config.keyboard)) {
2634 return false;
2635 }
2636 if (!match(AXIS_NAVIGATION, config.navigation)) {
2637 return false;
2638 }
2639 if (!match(AXIS_SCREENSIZE, config.screenSize)) {
2640 return false;
2641 }
f1f3915b
DH
2642 if (!match(AXIS_SMALLESTSCREENWIDTHDP, config.smallestScreenWidthDp)) {
2643 return false;
2644 }
4ee37df2
DH
2645 if (!match(AXIS_SCREENWIDTHDP, config.screenWidthDp)) {
2646 return false;
2647 }
2648 if (!match(AXIS_SCREENHEIGHTDP, config.screenHeightDp)) {
2649 return false;
2650 }
2651 if (!match(AXIS_SCREENLAYOUTSIZE, config.screenLayout&ResTable_config::MASK_SCREENSIZE)) {
2652 return false;
2653 }
a534180c
TAOSP
2654 if (!match(AXIS_VERSION, config.version)) {
2655 return false;
2656 }
2657 return true;
2658}
2659
2660status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
2661{
2662 ResourceFilter filter;
2663 status_t err = filter.parse(bundle->getConfigurations());
2664 if (err != NO_ERROR) {
2665 return err;
2666 }
2667
2668 const size_t N = mOrderedPackages.size();
2669 size_t pi;
2670
460ed1b2
KR
2671 const static String16 mipmap16("mipmap");
2672
1bad9d29 2673 bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
5af43148 2674
a534180c
TAOSP
2675 // Iterate through all data, collecting all values (strings,
2676 // references, etc).
5af43148 2677 StringPool valueStrings = StringPool(false, useUTF8);
a534180c
TAOSP
2678 for (pi=0; pi<N; pi++) {
2679 sp<Package> p = mOrderedPackages.itemAt(pi);
2680 if (p->getTypes().size() == 0) {
2681 // Empty, skip!
2682 continue;
2683 }
2684
5af43148
KR
2685 StringPool typeStrings = StringPool(false, useUTF8);
2686 StringPool keyStrings = StringPool(false, useUTF8);
a534180c
TAOSP
2687
2688 const size_t N = p->getOrderedTypes().size();
2689 for (size_t ti=0; ti<N; ti++) {
2690 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2691 if (t == NULL) {
2692 typeStrings.add(String16("<empty>"), false);
2693 continue;
2694 }
460ed1b2
KR
2695 const String16 typeName(t->getName());
2696 typeStrings.add(typeName, false);
2697
2698 const bool filterable = (typeName != mipmap16);
a534180c
TAOSP
2699
2700 const size_t N = t->getOrderedConfigs().size();
2701 for (size_t ci=0; ci<N; ci++) {
2702 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2703 if (c == NULL) {
2704 continue;
2705 }
2706 const size_t N = c->getEntries().size();
2707 for (size_t ei=0; ei<N; ei++) {
2708 ConfigDescription config = c->getEntries().keyAt(ei);
460ed1b2 2709 if (filterable && !filter.match(config)) {
a534180c
TAOSP
2710 continue;
2711 }
2712 sp<Entry> e = c->getEntries().valueAt(ei);
2713 if (e == NULL) {
2714 continue;
2715 }
2716 e->setNameIndex(keyStrings.add(e->getName(), true));
2717 status_t err = e->prepareFlatten(&valueStrings, this);
2718 if (err != NO_ERROR) {
2719 return err;
2720 }
2721 }
2722 }
2723 }
2724
2725 p->setTypeStrings(typeStrings.createStringBlock());
2726 p->setKeyStrings(keyStrings.createStringBlock());
2727 }
2728
2729 ssize_t strAmt = 0;
2730
2731 // Now build the array of package chunks.
2732 Vector<sp<AaptFile> > flatPackages;
2733 for (pi=0; pi<N; pi++) {
2734 sp<Package> p = mOrderedPackages.itemAt(pi);
2735 if (p->getTypes().size() == 0) {
2736 // Empty, skip!
2737 continue;
2738 }
2739
2740 const size_t N = p->getTypeStrings().size();
2741
2742 const size_t baseSize = sizeof(ResTable_package);
2743
2744 // Start the package data.
2745 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
2746 ResTable_package* header = (ResTable_package*)data->editData(baseSize);
2747 if (header == NULL) {
2748 fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
2749 return NO_MEMORY;
2750 }
2751 memset(header, 0, sizeof(*header));
2752 header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
2753 header->header.headerSize = htods(sizeof(*header));
2754 header->id = htodl(p->getAssignedId());
2755 strcpy16_htod(header->name, p->getName().string());
2756
2757 // Write the string blocks.
2758 const size_t typeStringsStart = data->getSize();
2759 sp<AaptFile> strFile = p->getTypeStringsData();
2760 ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
2761 #if PRINT_STRING_METRICS
2762 fprintf(stderr, "**** type strings: %d\n", amt);
2763 #endif
2764 strAmt += amt;
2765 if (amt < 0) {
2766 return amt;
2767 }
2768 const size_t keyStringsStart = data->getSize();
2769 strFile = p->getKeyStringsData();
2770 amt = data->writeData(strFile->getData(), strFile->getSize());
2771 #if PRINT_STRING_METRICS
2772 fprintf(stderr, "**** key strings: %d\n", amt);
2773 #endif
2774 strAmt += amt;
2775 if (amt < 0) {
2776 return amt;
2777 }
2778
2779 // Build the type chunks inside of this package.
2780 for (size_t ti=0; ti<N; ti++) {
2781 // Retrieve them in the same order as the type string block.
2782 size_t len;
2783 String16 typeName(p->getTypeStrings().stringAt(ti, &len));
2784 sp<Type> t = p->getTypes().valueFor(typeName);
2785 LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
2786 "Type name %s not found",
2787 String8(typeName).string());
2788
460ed1b2
KR
2789 const bool filterable = (typeName != mipmap16);
2790
a534180c
TAOSP
2791 const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
2792
2793 // First write the typeSpec chunk, containing information about
2794 // each resource entry in this type.
2795 {
2796 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
2797 const size_t typeSpecStart = data->getSize();
2798 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
2799 (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
2800 if (tsHeader == NULL) {
2801 fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
2802 return NO_MEMORY;
2803 }
2804 memset(tsHeader, 0, sizeof(*tsHeader));
2805 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
2806 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
2807 tsHeader->header.size = htodl(typeSpecSize);
2808 tsHeader->id = ti+1;
2809 tsHeader->entryCount = htodl(N);
2810
2811 uint32_t* typeSpecFlags = (uint32_t*)
2812 (((uint8_t*)data->editData())
2813 + typeSpecStart + sizeof(ResTable_typeSpec));
2814 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
460ed1b2 2815
a534180c
TAOSP
2816 for (size_t ei=0; ei<N; ei++) {
2817 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2818 if (cl->getPublic()) {
2819 typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
2820 }
2821 const size_t CN = cl->getEntries().size();
2822 for (size_t ci=0; ci<CN; ci++) {
460ed1b2 2823 if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
a534180c
TAOSP
2824 continue;
2825 }
2826 for (size_t cj=ci+1; cj<CN; cj++) {
460ed1b2 2827 if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
a534180c
TAOSP
2828 continue;
2829 }
2830 typeSpecFlags[ei] |= htodl(
2831 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
2832 }
2833 }
2834 }
2835 }
2836
2837 // We need to write one type chunk for each configuration for
2838 // which we have entries in this type.
2839 const size_t NC = t->getUniqueConfigs().size();
2840
2841 const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
2842
2843 for (size_t ci=0; ci<NC; ci++) {
2844 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
2845
2846 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
f1f3915b
DH
2847 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
2848 "sw%ddp w%ddp h%ddp\n",
a534180c
TAOSP
2849 ti+1,
2850 config.mcc, config.mnc,
2851 config.language[0] ? config.language[0] : '-',
2852 config.language[1] ? config.language[1] : '-',
2853 config.country[0] ? config.country[0] : '-',
2854 config.country[1] ? config.country[1] : '-',
2855 config.orientation,
2ca01a37 2856 config.uiMode,
a534180c
TAOSP
2857 config.touchscreen,
2858 config.density,
2859 config.keyboard,
2860 config.inputFlags,
2861 config.navigation,
2862 config.screenWidth,
4ee37df2 2863 config.screenHeight,
f1f3915b 2864 config.smallestScreenWidthDp,
4ee37df2
DH
2865 config.screenWidthDp,
2866 config.screenHeightDp));
a534180c 2867
460ed1b2 2868 if (filterable && !filter.match(config)) {
a534180c
TAOSP
2869 continue;
2870 }
2871
2872 const size_t typeStart = data->getSize();
2873
2874 ResTable_type* tHeader = (ResTable_type*)
2875 (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
2876 if (tHeader == NULL) {
2877 fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
2878 return NO_MEMORY;
2879 }
2880
2881 memset(tHeader, 0, sizeof(*tHeader));
2882 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
2883 tHeader->header.headerSize = htods(sizeof(*tHeader));
2884 tHeader->id = ti+1;
2885 tHeader->entryCount = htodl(N);
2886 tHeader->entriesStart = htodl(typeSize);
2887 tHeader->config = config;
2888 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
f1f3915b
DH
2889 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
2890 "sw%ddp w%ddp h%ddp\n",
a534180c
TAOSP
2891 ti+1,
2892 tHeader->config.mcc, tHeader->config.mnc,
2893 tHeader->config.language[0] ? tHeader->config.language[0] : '-',
2894 tHeader->config.language[1] ? tHeader->config.language[1] : '-',
2895 tHeader->config.country[0] ? tHeader->config.country[0] : '-',
2896 tHeader->config.country[1] ? tHeader->config.country[1] : '-',
2897 tHeader->config.orientation,
2ca01a37 2898 tHeader->config.uiMode,
a534180c
TAOSP
2899 tHeader->config.touchscreen,
2900 tHeader->config.density,
2901 tHeader->config.keyboard,
2902 tHeader->config.inputFlags,
2903 tHeader->config.navigation,
2904 tHeader->config.screenWidth,
4ee37df2 2905 tHeader->config.screenHeight,
f1f3915b 2906 tHeader->config.smallestScreenWidthDp,
4ee37df2
DH
2907 tHeader->config.screenWidthDp,
2908 tHeader->config.screenHeightDp));
a534180c
TAOSP
2909 tHeader->config.swapHtoD();
2910
2911 // Build the entries inside of this type.
2912 for (size_t ei=0; ei<N; ei++) {
2913 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2914 sp<Entry> e = cl->getEntries().valueFor(config);
2915
2916 // Set the offset for this entry in its type.
2917 uint32_t* index = (uint32_t*)
2918 (((uint8_t*)data->editData())
2919 + typeStart + sizeof(ResTable_type));
2920 if (e != NULL) {
2921 index[ei] = htodl(data->getSize()-typeStart-typeSize);
2922
2923 // Create the entry.
2924 ssize_t amt = e->flatten(bundle, data, cl->getPublic());
2925 if (amt < 0) {
2926 return amt;
2927 }
2928 } else {
2929 index[ei] = htodl(ResTable_type::NO_ENTRY);
2930 }
2931 }
2932
2933 // Fill in the rest of the type information.
2934 tHeader = (ResTable_type*)
2935 (((uint8_t*)data->editData()) + typeStart);
2936 tHeader->header.size = htodl(data->getSize()-typeStart);
2937 }
2938 }
2939
2940 // Fill in the rest of the package information.
2941 header = (ResTable_package*)data->editData();
2942 header->header.size = htodl(data->getSize());
2943 header->typeStrings = htodl(typeStringsStart);
2944 header->lastPublicType = htodl(p->getTypeStrings().size());
2945 header->keyStrings = htodl(keyStringsStart);
2946 header->lastPublicKey = htodl(p->getKeyStrings().size());
2947
2948 flatPackages.add(data);
2949 }
2950
2951 // And now write out the final chunks.
2952 const size_t dataStart = dest->getSize();
2953
2954 {
2955 // blah
2956 ResTable_header header;
2957 memset(&header, 0, sizeof(header));
2958 header.header.type = htods(RES_TABLE_TYPE);
2959 header.header.headerSize = htods(sizeof(header));
2960 header.packageCount = htodl(flatPackages.size());
2961 status_t err = dest->writeData(&header, sizeof(header));
2962 if (err != NO_ERROR) {
2963 fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
2964 return err;
2965 }
2966 }
2967
2968 ssize_t strStart = dest->getSize();
2969 err = valueStrings.writeStringBlock(dest);
2970 if (err != NO_ERROR) {
2971 return err;
2972 }
2973
2974 ssize_t amt = (dest->getSize()-strStart);
2975 strAmt += amt;
2976 #if PRINT_STRING_METRICS
2977 fprintf(stderr, "**** value strings: %d\n", amt);
2978 fprintf(stderr, "**** total strings: %d\n", strAmt);
2979 #endif
2980
2981 for (pi=0; pi<flatPackages.size(); pi++) {
2982 err = dest->writeData(flatPackages[pi]->getData(),
2983 flatPackages[pi]->getSize());
2984 if (err != NO_ERROR) {
2985 fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
2986 return err;
2987 }
2988 }
2989
2990 ResTable_header* header = (ResTable_header*)
2991 (((uint8_t*)dest->getData()) + dataStart);
2992 header->header.size = htodl(dest->getSize() - dataStart);
2993
2994 NOISY(aout << "Resource table:"
2995 << HexDump(dest->getData(), dest->getSize()) << endl);
2996
2997 #if PRINT_STRING_METRICS
2998 fprintf(stderr, "**** total resource table size: %d / %d%% strings\n",
2999 dest->getSize(), (strAmt*100)/dest->getSize());
3000 #endif
3001
3002 return NO_ERROR;
3003}
3004
3005void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
3006{
3007 fprintf(fp,
3008 "<!-- This file contains <public> resource definitions for all\n"
3009 " resources that were generated from the source data. -->\n"
3010 "\n"
3011 "<resources>\n");
3012
3013 writePublicDefinitions(package, fp, true);
3014 writePublicDefinitions(package, fp, false);
3015
3016 fprintf(fp,
3017 "\n"
3018 "</resources>\n");
3019}
3020
3021void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
3022{
3023 bool didHeader = false;
3024
3025 sp<Package> pkg = mPackages.valueFor(package);
3026 if (pkg != NULL) {
3027 const size_t NT = pkg->getOrderedTypes().size();
3028 for (size_t i=0; i<NT; i++) {
3029 sp<Type> t = pkg->getOrderedTypes().itemAt(i);
3030 if (t == NULL) {
3031 continue;
3032 }
3033
3034 bool didType = false;
3035
3036 const size_t NC = t->getOrderedConfigs().size();
3037 for (size_t j=0; j<NC; j++) {
3038 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
3039 if (c == NULL) {
3040 continue;
3041 }
3042
3043 if (c->getPublic() != pub) {
3044 continue;
3045 }
3046
3047 if (!didType) {
3048 fprintf(fp, "\n");
3049 didType = true;
3050 }
3051 if (!didHeader) {
3052 if (pub) {
3053 fprintf(fp," <!-- PUBLIC SECTION. These resources have been declared public.\n");
3054 fprintf(fp," Changes to these definitions will break binary compatibility. -->\n\n");
3055 } else {
3056 fprintf(fp," <!-- PRIVATE SECTION. These resources have not been declared public.\n");
3057 fprintf(fp," You can make them public my moving these lines into a file in res/values. -->\n\n");
3058 }
3059 didHeader = true;
3060 }
3061 if (!pub) {
3062 const size_t NE = c->getEntries().size();
3063 for (size_t k=0; k<NE; k++) {
3064 const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
3065 if (pos.file != "") {
3066 fprintf(fp," <!-- Declared at %s:%d -->\n",
3067 pos.file.string(), pos.line);
3068 }
3069 }
3070 }
3071 fprintf(fp, " <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
3072 String8(t->getName()).string(),
3073 String8(c->getName()).string(),
3074 getResId(pkg, t, c->getEntryIndex()));
3075 }
3076 }
3077 }
3078}
3079
3080ResourceTable::Item::Item(const SourcePos& _sourcePos,
3081 bool _isId,
3082 const String16& _value,
3083 const Vector<StringPool::entry_style_span>* _style,
3084 int32_t _format)
3085 : sourcePos(_sourcePos)
3086 , isId(_isId)
3087 , value(_value)
3088 , format(_format)
3089 , bagKeyId(0)
3090 , evaluating(false)
3091{
3092 if (_style) {
3093 style = *_style;
3094 }
3095}
3096
3097status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
3098{
3099 if (mType == TYPE_BAG) {
3100 return NO_ERROR;
3101 }
3102 if (mType == TYPE_UNKNOWN) {
3103 mType = TYPE_BAG;
3104 return NO_ERROR;
3105 }
3106 sourcePos.error("Resource entry %s is already defined as a single item.\n"
3107 "%s:%d: Originally defined here.\n",
3108 String8(mName).string(),
3109 mItem.sourcePos.file.string(), mItem.sourcePos.line);
3110 return UNKNOWN_ERROR;
3111}
3112
3113status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
3114 const String16& value,
3115 const Vector<StringPool::entry_style_span>* style,
3116 int32_t format,
3117 const bool overwrite)
3118{
3119 Item item(sourcePos, false, value, style);
3120
3121 if (mType == TYPE_BAG) {
3122 const Item& item(mBag.valueAt(0));
3123 sourcePos.error("Resource entry %s is already defined as a bag.\n"
3124 "%s:%d: Originally defined here.\n",
3125 String8(mName).string(),
3126 item.sourcePos.file.string(), item.sourcePos.line);
3127 return UNKNOWN_ERROR;
3128 }
3129 if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
3130 sourcePos.error("Resource entry %s is already defined.\n"
3131 "%s:%d: Originally defined here.\n",
3132 String8(mName).string(),
3133 mItem.sourcePos.file.string(), mItem.sourcePos.line);
3134 return UNKNOWN_ERROR;
3135 }
6c9f72e4 3136
a534180c
TAOSP
3137 mType = TYPE_ITEM;
3138 mItem = item;
3139 mItemFormat = format;
3140 return NO_ERROR;
3141}
3142
3143status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
3144 const String16& key, const String16& value,
3145 const Vector<StringPool::entry_style_span>* style,
3146 bool replace, bool isId, int32_t format)
3147{
3148 status_t err = makeItABag(sourcePos);
3149 if (err != NO_ERROR) {
3150 return err;
3151 }
3152
3153 Item item(sourcePos, isId, value, style, format);
3154
3155 // XXX NOTE: there is an error if you try to have a bag with two keys,
3156 // one an attr and one an id, with the same name. Not something we
3157 // currently ever have to worry about.
3158 ssize_t origKey = mBag.indexOfKey(key);
3159 if (origKey >= 0) {
3160 if (!replace) {
3161 const Item& item(mBag.valueAt(origKey));
3162 sourcePos.error("Resource entry %s already has bag item %s.\n"
3163 "%s:%d: Originally defined here.\n",
3164 String8(mName).string(), String8(key).string(),
3165 item.sourcePos.file.string(), item.sourcePos.line);
3166 return UNKNOWN_ERROR;
3167 }
3168 //printf("Replacing %s with %s\n",
3169 // String8(mBag.valueFor(key).value).string(), String8(value).string());
3170 mBag.replaceValueFor(key, item);
3171 }
3172
3173 mBag.add(key, item);
3174 return NO_ERROR;
3175}
3176
76486813
RG
3177status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
3178{
3179 status_t err = makeItABag(sourcePos);
3180 if (err != NO_ERROR) {
3181 return err;
3182 }
3183
3184 mBag.clear();
3185 return NO_ERROR;
3186}
3187
a534180c
TAOSP
3188status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
3189 const String16& package)
3190{
3191 const String16 attr16("attr");
3192 const String16 id16("id");
3193 const size_t N = mBag.size();
3194 for (size_t i=0; i<N; i++) {
3195 const String16& key = mBag.keyAt(i);
3196 const Item& it = mBag.valueAt(i);
3197 if (it.isId) {
3198 if (!table->hasBagOrEntry(key, &id16, &package)) {
3199 String16 value("false");
3200 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
3201 id16, key, value);
3202 if (err != NO_ERROR) {
3203 return err;
3204 }
3205 }
3206 } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
3207
3208#if 1
3209// fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
3210// String8(key).string());
3211// const Item& item(mBag.valueAt(i));
3212// fprintf(stderr, "Referenced from file %s line %d\n",
3213// item.sourcePos.file.string(), item.sourcePos.line);
3214// return UNKNOWN_ERROR;
3215#else
3216 char numberStr[16];
3217 sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
3218 status_t err = table->addBag(SourcePos("<generated>", 0), package,
3219 attr16, key, String16(""),
3220 String16("^type"),
3221 String16(numberStr), NULL, NULL);
3222 if (err != NO_ERROR) {
3223 return err;
3224 }
3225#endif
3226 }
3227 }
3228 return NO_ERROR;
3229}
3230
3231status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
3232 const String16& package)
3233{
3234 bool hasErrors = false;
3235
3236 if (mType == TYPE_BAG) {
3237 const char* errorMsg;
3238 const String16 style16("style");
3239 const String16 attr16("attr");
3240 const String16 id16("id");
3241 mParentId = 0;
3242 if (mParent.size() > 0) {
afc833f3 3243 mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
a534180c
TAOSP
3244 if (mParentId == 0) {
3245 mPos.error("Error retrieving parent for item: %s '%s'.\n",
3246 errorMsg, String8(mParent).string());
3247 hasErrors = true;
3248 }
3249 }
3250 const size_t N = mBag.size();
3251 for (size_t i=0; i<N; i++) {
3252 const String16& key = mBag.keyAt(i);
3253 Item& it = mBag.editValueAt(i);
3254 it.bagKeyId = table->getResId(key,
afc833f3 3255 it.isId ? &id16 : &attr16, NULL, &errorMsg);
a534180c
TAOSP
3256 //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
3257 if (it.bagKeyId == 0) {
3258 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
3259 String8(it.isId ? id16 : attr16).string(),
3260 String8(key).string());
3261 hasErrors = true;
3262 }
3263 }
3264 }
3265 return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
3266}
3267
3268status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
3269{
3270 if (mType == TYPE_ITEM) {
3271 Item& it = mItem;
3272 AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
3273 if (!table->stringToValue(&it.parsedValue, strings,
3274 it.value, false, true, 0,
3275 &it.style, NULL, &ac, mItemFormat)) {
3276 return UNKNOWN_ERROR;
3277 }
3278 } else if (mType == TYPE_BAG) {
3279 const size_t N = mBag.size();
3280 for (size_t i=0; i<N; i++) {
3281 const String16& key = mBag.keyAt(i);
3282 Item& it = mBag.editValueAt(i);
3283 AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
3284 if (!table->stringToValue(&it.parsedValue, strings,
3285 it.value, false, true, it.bagKeyId,
3286 &it.style, NULL, &ac, it.format)) {
3287 return UNKNOWN_ERROR;
3288 }
3289 }
3290 } else {
3291 mPos.error("Error: entry %s is not a single item or a bag.\n",
3292 String8(mName).string());
3293 return UNKNOWN_ERROR;
3294 }
3295 return NO_ERROR;
3296}
3297
3298ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
3299{
3300 size_t amt = 0;
3301 ResTable_entry header;
3302 memset(&header, 0, sizeof(header));
3303 header.size = htods(sizeof(header));
3304 const type ty = this != NULL ? mType : TYPE_ITEM;
3305 if (this != NULL) {
3306 if (ty == TYPE_BAG) {
3307 header.flags |= htods(header.FLAG_COMPLEX);
3308 }
3309 if (isPublic) {
3310 header.flags |= htods(header.FLAG_PUBLIC);
3311 }
3312 header.key.index = htodl(mNameIndex);
3313 }
3314 if (ty != TYPE_BAG) {
3315 status_t err = data->writeData(&header, sizeof(header));
3316 if (err != NO_ERROR) {
3317 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3318 return err;
3319 }
3320
3321 const Item& it = mItem;
3322 Res_value par;
3323 memset(&par, 0, sizeof(par));
3324 par.size = htods(it.parsedValue.size);
3325 par.dataType = it.parsedValue.dataType;
3326 par.res0 = it.parsedValue.res0;
3327 par.data = htodl(it.parsedValue.data);
3328 #if 0
3329 printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
3330 String8(mName).string(), it.parsedValue.dataType,
3331 it.parsedValue.data, par.res0);
3332 #endif
3333 err = data->writeData(&par, it.parsedValue.size);
3334 if (err != NO_ERROR) {
3335 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3336 return err;
3337 }
3338 amt += it.parsedValue.size;
3339 } else {
3340 size_t N = mBag.size();
3341 size_t i;
3342 // Create correct ordering of items.
3343 KeyedVector<uint32_t, const Item*> items;
3344 for (i=0; i<N; i++) {
3345 const Item& it = mBag.valueAt(i);
3346 items.add(it.bagKeyId, &it);
3347 }
3348 N = items.size();
3349
3350 ResTable_map_entry mapHeader;
3351 memcpy(&mapHeader, &header, sizeof(header));
3352 mapHeader.size = htods(sizeof(mapHeader));
3353 mapHeader.parent.ident = htodl(mParentId);
3354 mapHeader.count = htodl(N);
3355 status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
3356 if (err != NO_ERROR) {
3357 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3358 return err;
3359 }
3360
3361 for (i=0; i<N; i++) {
3362 const Item& it = *items.valueAt(i);
3363 ResTable_map map;
3364 map.name.ident = htodl(it.bagKeyId);
3365 map.value.size = htods(it.parsedValue.size);
3366 map.value.dataType = it.parsedValue.dataType;
3367 map.value.res0 = it.parsedValue.res0;
3368 map.value.data = htodl(it.parsedValue.data);
3369 err = data->writeData(&map, sizeof(map));
3370 if (err != NO_ERROR) {
3371 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3372 return err;
3373 }
3374 amt += sizeof(map);
3375 }
3376 }
3377 return amt;
3378}
3379
3380void ResourceTable::ConfigList::appendComment(const String16& comment,
3381 bool onlyIfEmpty)
3382{
3383 if (comment.size() <= 0) {
3384 return;
3385 }
3386 if (onlyIfEmpty && mComment.size() > 0) {
3387 return;
3388 }
3389 if (mComment.size() > 0) {
3390 mComment.append(String16("\n"));
3391 }
3392 mComment.append(comment);
3393}
3394
3395void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
3396{
3397 if (comment.size() <= 0) {
3398 return;
3399 }
3400 if (mTypeComment.size() > 0) {
3401 mTypeComment.append(String16("\n"));
3402 }
3403 mTypeComment.append(comment);
3404}
3405
3406status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
3407 const String16& name,
3408 const uint32_t ident)
3409{
3410 #if 0
3411 int32_t entryIdx = Res_GETENTRY(ident);
3412 if (entryIdx < 0) {
3413 sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
3414 String8(mName).string(), String8(name).string(), ident);
3415 return UNKNOWN_ERROR;
3416 }
3417 #endif
3418
3419 int32_t typeIdx = Res_GETTYPE(ident);
3420 if (typeIdx >= 0) {
3421 typeIdx++;
3422 if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
3423 sourcePos.error("Public resource %s/%s has conflicting type codes for its"
3424 " public identifiers (0x%x vs 0x%x).\n",
3425 String8(mName).string(), String8(name).string(),
3426 mPublicIndex, typeIdx);
3427 return UNKNOWN_ERROR;
3428 }
3429 mPublicIndex = typeIdx;
3430 }
3431
3432 if (mFirstPublicSourcePos == NULL) {
3433 mFirstPublicSourcePos = new SourcePos(sourcePos);
3434 }
3435
3436 if (mPublic.indexOfKey(name) < 0) {
3437 mPublic.add(name, Public(sourcePos, String16(), ident));
3438 } else {
3439 Public& p = mPublic.editValueFor(name);
3440 if (p.ident != ident) {
3441 sourcePos.error("Public resource %s/%s has conflicting public identifiers"
3442 " (0x%08x vs 0x%08x).\n"
3443 "%s:%d: Originally defined here.\n",
3444 String8(mName).string(), String8(name).string(), p.ident, ident,
3445 p.sourcePos.file.string(), p.sourcePos.line);
3446 return UNKNOWN_ERROR;
3447 }
3448 }
3449
3450 return NO_ERROR;
3451}
3452
a8c9cd56
DH
3453void ResourceTable::Type::canAddEntry(const String16& name)
3454{
3455 mCanAddEntries.add(name);
3456}
3457
a534180c
TAOSP
3458sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
3459 const SourcePos& sourcePos,
3460 const ResTable_config* config,
6c9f72e4 3461 bool doSetIndex,
636d3b70
XD
3462 bool overlay,
3463 bool autoAddOverlay)
a534180c
TAOSP
3464{
3465 int pos = -1;
3466 sp<ConfigList> c = mConfigs.valueFor(entry);
3467 if (c == NULL) {
636d3b70 3468 if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
a8c9cd56
DH
3469 sourcePos.error("Resource at %s appears in overlay but not"
3470 " in the base package; use <add-resource> to add.\n",
3471 String8(entry).string());
6c9f72e4
RG
3472 return NULL;
3473 }
a534180c
TAOSP
3474 c = new ConfigList(entry, sourcePos);
3475 mConfigs.add(entry, c);
3476 pos = (int)mOrderedConfigs.size();
3477 mOrderedConfigs.add(c);
3478 if (doSetIndex) {
3479 c->setEntryIndex(pos);
3480 }
3481 }
3482
3483 ConfigDescription cdesc;
3484 if (config) cdesc = *config;
3485
3486 sp<Entry> e = c->getEntries().valueFor(cdesc);
3487 if (e == NULL) {
3488 if (config != NULL) {
3489 NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
f1f3915b
DH
3490 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
3491 "sw%ddp w%ddp h%ddp\n",
a534180c
TAOSP
3492 sourcePos.file.string(), sourcePos.line,
3493 config->mcc, config->mnc,
3494 config->language[0] ? config->language[0] : '-',
3495 config->language[1] ? config->language[1] : '-',
3496 config->country[0] ? config->country[0] : '-',
3497 config->country[1] ? config->country[1] : '-',
3498 config->orientation,
3499 config->touchscreen,
3500 config->density,
3501 config->keyboard,
3502 config->inputFlags,
3503 config->navigation,
3504 config->screenWidth,
4ee37df2 3505 config->screenHeight,
f1f3915b 3506 config->smallestScreenWidthDp,
4ee37df2
DH
3507 config->screenWidthDp,
3508 config->screenHeightDp));
a534180c
TAOSP
3509 } else {
3510 NOISY(printf("New entry at %s:%d: NULL config\n",
3511 sourcePos.file.string(), sourcePos.line));
3512 }
3513 e = new Entry(entry, sourcePos);
3514 c->addEntry(cdesc, e);
3515 /*
3516 if (doSetIndex) {
3517 if (pos < 0) {
3518 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
3519 if (mOrderedConfigs[pos] == c) {
3520 break;
3521 }
3522 }
3523 if (pos >= (int)mOrderedConfigs.size()) {
3524 sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
3525 return NULL;
3526 }
3527 }
3528 e->setEntryIndex(pos);
3529 }
3530 */
3531 }
3532
3533 mUniqueConfigs.add(cdesc);
3534
3535 return e;
3536}
3537
3538status_t ResourceTable::Type::applyPublicEntryOrder()
3539{
3540 size_t N = mOrderedConfigs.size();
3541 Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
3542 bool hasError = false;
3543
3544 size_t i;
3545 for (i=0; i<N; i++) {
3546 mOrderedConfigs.replaceAt(NULL, i);
3547 }
3548
3549 const size_t NP = mPublic.size();
3550 //printf("Ordering %d configs from %d public defs\n", N, NP);
3551 size_t j;
3552 for (j=0; j<NP; j++) {
3553 const String16& name = mPublic.keyAt(j);
3554 const Public& p = mPublic.valueAt(j);
3555 int32_t idx = Res_GETENTRY(p.ident);
3556 //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
3557 // String8(mName).string(), String8(name).string(), p.ident, N);
3558 bool found = false;
3559 for (i=0; i<N; i++) {
3560 sp<ConfigList> e = origOrder.itemAt(i);
3561 //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
3562 if (e->getName() == name) {
3563 if (idx >= (int32_t)mOrderedConfigs.size()) {
3564 p.sourcePos.error("Public entry identifier 0x%x entry index "
3565 "is larger than available symbols (index %d, total symbols %d).\n",
3566 p.ident, idx, mOrderedConfigs.size());
3567 hasError = true;
3568 } else if (mOrderedConfigs.itemAt(idx) == NULL) {
3569 e->setPublic(true);
3570 e->setPublicSourcePos(p.sourcePos);
3571 mOrderedConfigs.replaceAt(e, idx);
3572 origOrder.removeAt(i);
3573 N--;
3574 found = true;
3575 break;
3576 } else {
3577 sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
3578
3579 p.sourcePos.error("Multiple entry names declared for public entry"
3580 " identifier 0x%x in type %s (%s vs %s).\n"
3581 "%s:%d: Originally defined here.",
3582 idx+1, String8(mName).string(),
3583 String8(oe->getName()).string(),
3584 String8(name).string(),
3585 oe->getPublicSourcePos().file.string(),
3586 oe->getPublicSourcePos().line);
3587 hasError = true;
3588 }
3589 }
3590 }
3591
3592 if (!found) {
3593 p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
3594 String8(mName).string(), String8(name).string());
3595 hasError = true;
3596 }
3597 }
3598
3599 //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
3600
3601 if (N != origOrder.size()) {
3602 printf("Internal error: remaining private symbol count mismatch\n");
3603 N = origOrder.size();
3604 }
3605
3606 j = 0;
3607 for (i=0; i<N; i++) {
3608 sp<ConfigList> e = origOrder.itemAt(i);
3609 // There will always be enough room for the remaining entries.
3610 while (mOrderedConfigs.itemAt(j) != NULL) {
3611 j++;
3612 }
3613 mOrderedConfigs.replaceAt(e, j);
3614 j++;
3615 }
3616
3617 return hasError ? UNKNOWN_ERROR : NO_ERROR;
3618}
3619
3620ResourceTable::Package::Package(const String16& name, ssize_t includedId)
3621 : mName(name), mIncludedId(includedId),
3622 mTypeStringsMapping(0xffffffff),
3623 mKeyStringsMapping(0xffffffff)
3624{
3625}
3626
3627sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
3628 const SourcePos& sourcePos,
3629 bool doSetIndex)
3630{
3631 sp<Type> t = mTypes.valueFor(type);
3632 if (t == NULL) {
3633 t = new Type(type, sourcePos);
3634 mTypes.add(type, t);
3635 mOrderedTypes.add(t);
3636 if (doSetIndex) {
3637 // For some reason the type's index is set to one plus the index
3638 // in the mOrderedTypes list, rather than just the index.
3639 t->setIndex(mOrderedTypes.size());
3640 }
3641 }
3642 return t;
3643}
3644
3645status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
3646{
3647 mTypeStringsData = data;
3648 status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
3649 if (err != NO_ERROR) {
3650 fprintf(stderr, "ERROR: Type string data is corrupt!\n");
3651 }
3652 return err;
3653}
3654
3655status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
3656{
3657 mKeyStringsData = data;
3658 status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
3659 if (err != NO_ERROR) {
3660 fprintf(stderr, "ERROR: Key string data is corrupt!\n");
3661 }
3662 return err;
3663}
3664
3665status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
3666 ResStringPool* strings,
3667 DefaultKeyedVector<String16, uint32_t>* mappings)
3668{
3669 if (data->getData() == NULL) {
3670 return UNKNOWN_ERROR;
3671 }
3672
3673 NOISY(aout << "Setting restable string pool: "
3674 << HexDump(data->getData(), data->getSize()) << endl);
3675
3676 status_t err = strings->setTo(data->getData(), data->getSize());
3677 if (err == NO_ERROR) {
3678 const size_t N = strings->size();
3679 for (size_t i=0; i<N; i++) {
3680 size_t len;
3681 mappings->add(String16(strings->stringAt(i, &len)), i);
3682 }
3683 }
3684 return err;
3685}
3686
3687status_t ResourceTable::Package::applyPublicTypeOrder()
3688{
3689 size_t N = mOrderedTypes.size();
3690 Vector<sp<Type> > origOrder(mOrderedTypes);
3691
3692 size_t i;
3693 for (i=0; i<N; i++) {
3694 mOrderedTypes.replaceAt(NULL, i);
3695 }
3696
3697 for (i=0; i<N; i++) {
3698 sp<Type> t = origOrder.itemAt(i);
3699 int32_t idx = t->getPublicIndex();
3700 if (idx > 0) {
3701 idx--;
3702 while (idx >= (int32_t)mOrderedTypes.size()) {
3703 mOrderedTypes.add();
3704 }
3705 if (mOrderedTypes.itemAt(idx) != NULL) {
3706 sp<Type> ot = mOrderedTypes.itemAt(idx);
3707 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
3708 " identifier 0x%x (%s vs %s).\n"
3709 "%s:%d: Originally defined here.",
3710 idx, String8(ot->getName()).string(),
3711 String8(t->getName()).string(),
3712 ot->getFirstPublicSourcePos().file.string(),
3713 ot->getFirstPublicSourcePos().line);
3714 return UNKNOWN_ERROR;
3715 }
3716 mOrderedTypes.replaceAt(t, idx);
3717 origOrder.removeAt(i);
3718 i--;
3719 N--;
3720 }
3721 }
3722
3723 size_t j=0;
3724 for (i=0; i<N; i++) {
3725 sp<Type> t = origOrder.itemAt(i);
3726 // There will always be enough room for the remaining types.
3727 while (mOrderedTypes.itemAt(j) != NULL) {
3728 j++;
3729 }
3730 mOrderedTypes.replaceAt(t, j);
3731 }
3732
3733 return NO_ERROR;
3734}
3735
3736sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
3737{
3738 sp<Package> p = mPackages.valueFor(package);
3739 if (p == NULL) {
b1bccba1
MK
3740 if (mBundle->getIsOverlayPackage()) {
3741 p = new Package(package, 0x00);
3742 } else if (mIsAppPackage) {
a534180c
TAOSP
3743 if (mHaveAppPackage) {
3744 fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
3745 "Use -x to create extended resources.\n");
3746 return NULL;
3747 }
3748 mHaveAppPackage = true;
3749 p = new Package(package, 127);
3750 } else {
3751 p = new Package(package, mNextPackageId);
3752 }
3753 //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
3754 // String8(package).string(), p->getAssignedId());
3755 mPackages.add(package, p);
3756 mOrderedPackages.add(p);
3757 mNextPackageId++;
3758 }
3759 return p;
3760}
3761
3762sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
3763 const String16& type,
3764 const SourcePos& sourcePos,
3765 bool doSetIndex)
3766{
3767 sp<Package> p = getPackage(package);
3768 if (p == NULL) {
3769 return NULL;
3770 }
3771 return p->getType(type, sourcePos, doSetIndex);
3772}
3773
3774sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
3775 const String16& type,
3776 const String16& name,
3777 const SourcePos& sourcePos,
6c9f72e4 3778 bool overlay,
a534180c
TAOSP
3779 const ResTable_config* config,
3780 bool doSetIndex)
3781{
3782 sp<Type> t = getType(package, type, sourcePos, doSetIndex);
3783 if (t == NULL) {
3784 return NULL;
3785 }
636d3b70 3786 return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
a534180c
TAOSP
3787}
3788
3789sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
3790 const ResTable_config* config) const
3791{
3792 int pid = Res_GETPACKAGE(resID)+1;
3793 const size_t N = mOrderedPackages.size();
3794 size_t i;
3795 sp<Package> p;
3796 for (i=0; i<N; i++) {
3797 sp<Package> check = mOrderedPackages[i];
3798 if (check->getAssignedId() == pid) {
3799 p = check;
3800 break;
3801 }
3802
3803 }
3804 if (p == NULL) {
406a85e3 3805 fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
a534180c
TAOSP
3806 return NULL;
3807 }
3808
3809 int tid = Res_GETTYPE(resID);
3810 if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
406a85e3 3811 fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
a534180c
TAOSP
3812 return NULL;
3813 }
3814 sp<Type> t = p->getOrderedTypes()[tid];
3815
3816 int eid = Res_GETENTRY(resID);
3817 if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
406a85e3 3818 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
a534180c
TAOSP
3819 return NULL;
3820 }
3821
3822 sp<ConfigList> c = t->getOrderedConfigs()[eid];
3823 if (c == NULL) {
406a85e3 3824 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
a534180c
TAOSP
3825 return NULL;
3826 }
3827
3828 ConfigDescription cdesc;
3829 if (config) cdesc = *config;
3830 sp<Entry> e = c->getEntries().valueFor(cdesc);
3831 if (c == NULL) {
406a85e3 3832 fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
a534180c
TAOSP
3833 return NULL;
3834 }
3835
3836 return e;
3837}
3838
3839const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
3840{
3841 sp<const Entry> e = getEntry(resID);
3842 if (e == NULL) {
3843 return NULL;
3844 }
3845
3846 const size_t N = e->getBag().size();
3847 for (size_t i=0; i<N; i++) {
3848 const Item& it = e->getBag().valueAt(i);
3849 if (it.bagKeyId == 0) {
406a85e3 3850 fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
a534180c
TAOSP
3851 String8(e->getName()).string(),
3852 String8(e->getBag().keyAt(i)).string());
3853 }
3854 if (it.bagKeyId == attrID) {
3855 return &it;
3856 }
3857 }
3858
3859 return NULL;
3860}
3861
3862bool ResourceTable::getItemValue(
3863 uint32_t resID, uint32_t attrID, Res_value* outValue)
3864{
3865 const Item* item = getItem(resID, attrID);
3866
3867 bool res = false;
3868 if (item != NULL) {
3869 if (item->evaluating) {
3870 sp<const Entry> e = getEntry(resID);
3871 const size_t N = e->getBag().size();
3872 size_t i;
3873 for (i=0; i<N; i++) {
3874 if (&e->getBag().valueAt(i) == item) {
3875 break;
3876 }
3877 }
406a85e3 3878 fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
a534180c
TAOSP
3879 String8(e->getName()).string(),
3880 String8(e->getBag().keyAt(i)).string());
3881 return false;
3882 }
3883 item->evaluating = true;
3884 res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
3885 NOISY(
3886 if (res) {
3887 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
3888 resID, attrID, String8(getEntry(resID)->getName()).string(),
3889 outValue->dataType, outValue->data);
3890 } else {
3891 printf("getItemValue of #%08x[#%08x]: failed\n",
3892 resID, attrID);
3893 }
3894 );
3895 item->evaluating = false;
3896 }
3897 return res;
3898}