]> git.saurik.com Git - android/aapt.git/blame - ResourceTable.cpp
am 820caf7b: am 8821ba86: Ummm... f*, stop breaking the build.
[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 &&
698 !outTable->hasBagOrEntry(myPackage, curType, ident)) {
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
1826bool ResourceTable::hasBagOrEntry(const String16& ref,
1827 const String16* defType,
1828 const String16* defPackage)
1829{
1830 String16 package, type, name;
1831 if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
1832 defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
1833 return false;
1834 }
1835 return hasBagOrEntry(package, type, name);
1836}
1837
1838bool ResourceTable::appendComment(const String16& package,
1839 const String16& type,
1840 const String16& name,
1841 const String16& comment,
1842 bool onlyIfEmpty)
1843{
1844 if (comment.size() <= 0) {
1845 return true;
1846 }
1847
1848 sp<Package> p = mPackages.valueFor(package);
1849 if (p != NULL) {
1850 sp<Type> t = p->getTypes().valueFor(type);
1851 if (t != NULL) {
1852 sp<ConfigList> c = t->getConfigs().valueFor(name);
1853 if (c != NULL) {
1854 c->appendComment(comment, onlyIfEmpty);
1855 return true;
1856 }
1857 }
1858 }
1859 return false;
1860}
1861
1862bool ResourceTable::appendTypeComment(const String16& package,
1863 const String16& type,
1864 const String16& name,
1865 const String16& comment)
1866{
1867 if (comment.size() <= 0) {
1868 return true;
1869 }
1870
1871 sp<Package> p = mPackages.valueFor(package);
1872 if (p != NULL) {
1873 sp<Type> t = p->getTypes().valueFor(type);
1874 if (t != NULL) {
1875 sp<ConfigList> c = t->getConfigs().valueFor(name);
1876 if (c != NULL) {
1877 c->appendTypeComment(comment);
1878 return true;
1879 }
1880 }
1881 }
1882 return false;
1883}
1884
a8c9cd56
DH
1885void ResourceTable::canAddEntry(const SourcePos& pos,
1886 const String16& package, const String16& type, const String16& name)
1887{
1888 sp<Type> t = getType(package, type, pos);
1889 if (t != NULL) {
1890 t->canAddEntry(name);
1891 }
1892}
1893
a534180c
TAOSP
1894size_t ResourceTable::size() const {
1895 return mPackages.size();
1896}
1897
1898size_t ResourceTable::numLocalResources() const {
1899 return mNumLocal;
1900}
1901
1902bool ResourceTable::hasResources() const {
1903 return mNumLocal > 0;
1904}
1905
1906sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
1907{
1908 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
1909 status_t err = flatten(bundle, data);
1910 return err == NO_ERROR ? data : NULL;
1911}
1912
1913inline uint32_t ResourceTable::getResId(const sp<Package>& p,
1914 const sp<Type>& t,
1915 uint32_t nameId)
1916{
1917 return makeResId(p->getAssignedId(), t->getIndex(), nameId);
1918}
1919
1920uint32_t ResourceTable::getResId(const String16& package,
1921 const String16& type,
1922 const String16& name,
1923 bool onlyPublic) const
1924{
1925 sp<Package> p = mPackages.valueFor(package);
1926 if (p == NULL) return 0;
1927
1928 // First look for this in the included resources...
1929 uint32_t specFlags = 0;
1930 uint32_t rid = mAssets->getIncludedResources()
1931 .identifierForName(name.string(), name.size(),
1932 type.string(), type.size(),
1933 package.string(), package.size(),
1934 &specFlags);
1935 if (rid != 0) {
1936 if (onlyPublic) {
1937 if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
1938 return 0;
1939 }
1940 }
1941
1942 if (Res_INTERNALID(rid)) {
1943 return rid;
1944 }
1945 return Res_MAKEID(p->getAssignedId()-1,
1946 Res_GETTYPE(rid),
1947 Res_GETENTRY(rid));
1948 }
1949
1950 sp<Type> t = p->getTypes().valueFor(type);
1951 if (t == NULL) return 0;
1952 sp<ConfigList> c = t->getConfigs().valueFor(name);
1953 if (c == NULL) return 0;
1954 int32_t ei = c->getEntryIndex();
1955 if (ei < 0) return 0;
1956 return getResId(p, t, ei);
1957}
1958
1959uint32_t ResourceTable::getResId(const String16& ref,
1960 const String16* defType,
1961 const String16* defPackage,
1962 const char** outErrorMsg,
1963 bool onlyPublic) const
1964{
1965 String16 package, type, name;
2b01c9f2 1966 bool refOnlyPublic = true;
a534180c
TAOSP
1967 if (!ResTable::expandResourceRef(
1968 ref.string(), ref.size(), &package, &type, &name,
1969 defType, defPackage ? defPackage:&mAssetsPackage,
2b01c9f2 1970 outErrorMsg, &refOnlyPublic)) {
a534180c
TAOSP
1971 NOISY(printf("Expanding resource: ref=%s\n",
1972 String8(ref).string()));
1973 NOISY(printf("Expanding resource: defType=%s\n",
1974 defType ? String8(*defType).string() : "NULL"));
1975 NOISY(printf("Expanding resource: defPackage=%s\n",
1976 defPackage ? String8(*defPackage).string() : "NULL"));
1977 NOISY(printf("Expanding resource: ref=%s\n", String8(ref).string()));
1978 NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
1979 String8(package).string(), String8(type).string(),
1980 String8(name).string()));
1981 return 0;
1982 }
2b01c9f2 1983 uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic);
a534180c
TAOSP
1984 NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
1985 String8(package).string(), String8(type).string(),
1986 String8(name).string(), res));
1987 if (res == 0) {
1988 if (outErrorMsg)
1989 *outErrorMsg = "No resource found that matches the given name";
1990 }
1991 return res;
1992}
1993
1994bool ResourceTable::isValidResourceName(const String16& s)
1995{
1996 const char16_t* p = s.string();
1997 bool first = true;
1998 while (*p) {
1999 if ((*p >= 'a' && *p <= 'z')
2000 || (*p >= 'A' && *p <= 'Z')
2001 || *p == '_'
2002 || (!first && *p >= '0' && *p <= '9')) {
2003 first = false;
2004 p++;
2005 continue;
2006 }
2007 return false;
2008 }
2009 return true;
2010}
2011
2012bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
2013 const String16& str,
2014 bool preserveSpaces, bool coerceType,
2015 uint32_t attrID,
2016 const Vector<StringPool::entry_style_span>* style,
2017 String16* outStr, void* accessorCookie,
2018 uint32_t attrType)
2019{
2020 String16 finalStr;
2021
2022 bool res = true;
2023 if (style == NULL || style->size() == 0) {
2024 // Text is not styled so it can be any type... let's figure it out.
2025 res = mAssets->getIncludedResources()
2026 .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
2027 coerceType, attrID, NULL, &mAssetsPackage, this,
2028 accessorCookie, attrType);
2029 } else {
2030 // Styled text can only be a string, and while collecting the style
2031 // information we have already processed that string!
2032 outValue->size = sizeof(Res_value);
2033 outValue->res0 = 0;
84069cb2 2034 outValue->dataType = outValue->TYPE_STRING;
a534180c
TAOSP
2035 outValue->data = 0;
2036 finalStr = str;
2037 }
2038
2039 if (!res) {
2040 return false;
2041 }
2042
84069cb2 2043 if (outValue->dataType == outValue->TYPE_STRING) {
a534180c
TAOSP
2044 // Should do better merging styles.
2045 if (pool) {
2046 if (style != NULL && style->size() > 0) {
2047 outValue->data = pool->add(finalStr, *style);
2048 } else {
2049 outValue->data = pool->add(finalStr, true);
2050 }
2051 } else {
2052 // Caller will fill this in later.
2053 outValue->data = 0;
2054 }
2055
2056 if (outStr) {
2057 *outStr = finalStr;
2058 }
2059
2060 }
2061
2062 return true;
2063}
2064
2065uint32_t ResourceTable::getCustomResource(
2066 const String16& package, const String16& type, const String16& name) const
2067{
2068 //printf("getCustomResource: %s %s %s\n", String8(package).string(),
2069 // String8(type).string(), String8(name).string());
2070 sp<Package> p = mPackages.valueFor(package);
2071 if (p == NULL) return 0;
2072 sp<Type> t = p->getTypes().valueFor(type);
2073 if (t == NULL) return 0;
2074 sp<ConfigList> c = t->getConfigs().valueFor(name);
2075 if (c == NULL) return 0;
2076 int32_t ei = c->getEntryIndex();
2077 if (ei < 0) return 0;
2078 return getResId(p, t, ei);
2079}
2080
2081uint32_t ResourceTable::getCustomResourceWithCreation(
2082 const String16& package, const String16& type, const String16& name,
2083 const bool createIfNotFound)
2084{
2085 uint32_t resId = getCustomResource(package, type, name);
2086 if (resId != 0 || !createIfNotFound) {
2087 return resId;
2088 }
2089 String16 value("false");
2090
2091 status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
2092 if (status == NO_ERROR) {
2093 resId = getResId(package, type, name);
2094 return resId;
2095 }
2096 return 0;
2097}
2098
2099uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
2100{
2101 return origPackage;
2102}
2103
2104bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
2105{
2106 //printf("getAttributeType #%08x\n", attrID);
2107 Res_value value;
2108 if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
2109 //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
2110 // String8(getEntry(attrID)->getName()).string(), value.data);
2111 *outType = value.data;
2112 return true;
2113 }
2114 return false;
2115}
2116
2117bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
2118{
2119 //printf("getAttributeMin #%08x\n", attrID);
2120 Res_value value;
2121 if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
2122 *outMin = value.data;
2123 return true;
2124 }
2125 return false;
2126}
2127
2128bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
2129{
2130 //printf("getAttributeMax #%08x\n", attrID);
2131 Res_value value;
2132 if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
2133 *outMax = value.data;
2134 return true;
2135 }
2136 return false;
2137}
2138
2139uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
2140{
2141 //printf("getAttributeL10N #%08x\n", attrID);
2142 Res_value value;
2143 if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
2144 return value.data;
2145 }
2146 return ResTable_map::L10N_NOT_REQUIRED;
2147}
2148
2149bool ResourceTable::getLocalizationSetting()
2150{
2151 return mBundle->getRequireLocalization();
2152}
2153
2154void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
2155{
2156 if (accessorCookie != NULL && fmt != NULL) {
2157 AccessorCookie* ac = (AccessorCookie*)accessorCookie;
2158 int retval=0;
2159 char buf[1024];
2160 va_list ap;
2161 va_start(ap, fmt);
2162 retval = vsnprintf(buf, sizeof(buf), fmt, ap);
2163 va_end(ap);
2164 ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
2165 buf, ac->attr.string(), ac->value.string());
2166 }
2167}
2168
2169bool ResourceTable::getAttributeKeys(
2170 uint32_t attrID, Vector<String16>* outKeys)
2171{
2172 sp<const Entry> e = getEntry(attrID);
2173 if (e != NULL) {
2174 const size_t N = e->getBag().size();
2175 for (size_t i=0; i<N; i++) {
2176 const String16& key = e->getBag().keyAt(i);
2177 if (key.size() > 0 && key.string()[0] != '^') {
2178 outKeys->add(key);
2179 }
2180 }
2181 return true;
2182 }
2183 return false;
2184}
2185
2186bool ResourceTable::getAttributeEnum(
2187 uint32_t attrID, const char16_t* name, size_t nameLen,
2188 Res_value* outValue)
2189{
2190 //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
2191 String16 nameStr(name, nameLen);
2192 sp<const Entry> e = getEntry(attrID);
2193 if (e != NULL) {
2194 const size_t N = e->getBag().size();
2195 for (size_t i=0; i<N; i++) {
2196 //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
2197 // String8(e->getBag().keyAt(i)).string());
2198 if (e->getBag().keyAt(i) == nameStr) {
2199 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
2200 }
2201 }
2202 }
2203 return false;
2204}
2205
2206bool ResourceTable::getAttributeFlags(
2207 uint32_t attrID, const char16_t* name, size_t nameLen,
2208 Res_value* outValue)
2209{
2210 outValue->dataType = Res_value::TYPE_INT_HEX;
2211 outValue->data = 0;
2212
2213 //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
2214 String16 nameStr(name, nameLen);
2215 sp<const Entry> e = getEntry(attrID);
2216 if (e != NULL) {
2217 const size_t N = e->getBag().size();
2218
2219 const char16_t* end = name + nameLen;
2220 const char16_t* pos = name;
2221 bool failed = false;
2222 while (pos < end && !failed) {
2223 const char16_t* start = pos;
2224 end++;
2225 while (pos < end && *pos != '|') {
2226 pos++;
2227 }
2228
2229 String16 nameStr(start, pos-start);
2230 size_t i;
2231 for (i=0; i<N; i++) {
2232 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
2233 // String8(e->getBag().keyAt(i)).string());
2234 if (e->getBag().keyAt(i) == nameStr) {
2235 Res_value val;
2236 bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
2237 if (!got) {
2238 return false;
2239 }
2240 //printf("Got value: 0x%08x\n", val.data);
2241 outValue->data |= val.data;
2242 break;
2243 }
2244 }
2245
2246 if (i >= N) {
2247 // Didn't find this flag identifier.
2248 return false;
2249 }
2250 if (pos < end) {
2251 pos++;
2252 }
2253 }
2254
2255 return true;
2256 }
2257 return false;
2258}
2259
2260status_t ResourceTable::assignResourceIds()
2261{
2262 const size_t N = mOrderedPackages.size();
2263 size_t pi;
2264 status_t firstError = NO_ERROR;
2265
2266 // First generate all bag attributes and assign indices.
2267 for (pi=0; pi<N; pi++) {
2268 sp<Package> p = mOrderedPackages.itemAt(pi);
2269 if (p == NULL || p->getTypes().size() == 0) {
2270 // Empty, skip!
2271 continue;
2272 }
2273
2274 status_t err = p->applyPublicTypeOrder();
2275 if (err != NO_ERROR && firstError == NO_ERROR) {
2276 firstError = err;
2277 }
2278
2279 // Generate attributes...
2280 const size_t N = p->getOrderedTypes().size();
2281 size_t ti;
2282 for (ti=0; ti<N; ti++) {
2283 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2284 if (t == NULL) {
2285 continue;
2286 }
2287 const size_t N = t->getOrderedConfigs().size();
2288 for (size_t ci=0; ci<N; ci++) {
2289 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2290 if (c == NULL) {
2291 continue;
2292 }
2293 const size_t N = c->getEntries().size();
2294 for (size_t ei=0; ei<N; ei++) {
2295 sp<Entry> e = c->getEntries().valueAt(ei);
2296 if (e == NULL) {
2297 continue;
2298 }
2299 status_t err = e->generateAttributes(this, p->getName());
2300 if (err != NO_ERROR && firstError == NO_ERROR) {
2301 firstError = err;
2302 }
2303 }
2304 }
2305 }
2306
2307 const SourcePos unknown(String8("????"), 0);
2308 sp<Type> attr = p->getType(String16("attr"), unknown);
2309
2310 // Assign indices...
2311 for (ti=0; ti<N; ti++) {
2312 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2313 if (t == NULL) {
2314 continue;
2315 }
2316 err = t->applyPublicEntryOrder();
2317 if (err != NO_ERROR && firstError == NO_ERROR) {
2318 firstError = err;
2319 }
2320
2321 const size_t N = t->getOrderedConfigs().size();
2322 t->setIndex(ti+1);
2323
2324 LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
2325 "First type is not attr!");
2326
2327 for (size_t ei=0; ei<N; ei++) {
2328 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
2329 if (c == NULL) {
2330 continue;
2331 }
2332 c->setEntryIndex(ei);
2333 }
2334 }
2335
2336 // Assign resource IDs to keys in bags...
2337 for (ti=0; ti<N; ti++) {
2338 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2339 if (t == NULL) {
2340 continue;
2341 }
2342 const size_t N = t->getOrderedConfigs().size();
2343 for (size_t ci=0; ci<N; ci++) {
2344 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2345 //printf("Ordered config #%d: %p\n", ci, c.get());
2346 const size_t N = c->getEntries().size();
2347 for (size_t ei=0; ei<N; ei++) {
2348 sp<Entry> e = c->getEntries().valueAt(ei);
2349 if (e == NULL) {
2350 continue;
2351 }
2352 status_t err = e->assignResourceIds(this, p->getName());
2353 if (err != NO_ERROR && firstError == NO_ERROR) {
2354 firstError = err;
2355 }
2356 }
2357 }
2358 }
2359 }
2360 return firstError;
2361}
2362
2363status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
2364 const size_t N = mOrderedPackages.size();
2365 size_t pi;
2366
2367 for (pi=0; pi<N; pi++) {
2368 sp<Package> p = mOrderedPackages.itemAt(pi);
2369 if (p->getTypes().size() == 0) {
2370 // Empty, skip!
2371 continue;
2372 }
2373
2374 const size_t N = p->getOrderedTypes().size();
2375 size_t ti;
2376
2377 for (ti=0; ti<N; ti++) {
2378 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2379 if (t == NULL) {
2380 continue;
2381 }
2382 const size_t N = t->getOrderedConfigs().size();
2383 sp<AaptSymbols> typeSymbols;
2384 typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
2385 for (size_t ci=0; ci<N; ci++) {
2386 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2387 if (c == NULL) {
2388 continue;
2389 }
2390 uint32_t rid = getResId(p, t, ci);
2391 if (rid == 0) {
2392 return UNKNOWN_ERROR;
2393 }
2394 if (Res_GETPACKAGE(rid) == (size_t)(p->getAssignedId()-1)) {
2395 typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
2396
2397 String16 comment(c->getComment());
2398 typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
2399 //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(),
2400 // String8(comment).string());
2401 comment = c->getTypeComment();
2402 typeSymbols->appendTypeComment(String8(c->getName()), comment);
2403 } else {
2404#if 0
2405 printf("**** NO MATCH: 0x%08x vs 0x%08x\n",
2406 Res_GETPACKAGE(rid), p->getAssignedId());
2407#endif
2408 }
2409 }
2410 }
2411 }
2412 return NO_ERROR;
2413}
2414
2415
2416void
2417ResourceTable::addLocalization(const String16& name, const String8& locale)
2418{
2419 mLocalizations[name].insert(locale);
2420}
2421
2422
2423/*!
2424 * Flag various sorts of localization problems. '+' indicates checks already implemented;
2425 * '-' indicates checks that will be implemented in the future.
2426 *
2427 * + A localized string for which no default-locale version exists => warning
2428 * + A string for which no version in an explicitly-requested locale exists => warning
2429 * + A localized translation of an translateable="false" string => warning
2430 * - A localized string not provided in every locale used by the table
2431 */
2432status_t
2433ResourceTable::validateLocalizations(void)
2434{
2435 status_t err = NO_ERROR;
2436 const String8 defaultLocale;
2437
2438 // For all strings...
2439 for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
2440 nameIter != mLocalizations.end();
2441 nameIter++) {
2442 const set<String8>& configSet = nameIter->second; // naming convenience
2443
2444 // Look for strings with no default localization
2445 if (configSet.count(defaultLocale) == 0) {
2446 fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
2447 String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
c70f9935 2448 for (set<String8>::const_iterator locales = configSet.begin();
a534180c
TAOSP
2449 locales != configSet.end();
2450 locales++) {
2451 fprintf(stdout, " %s", (*locales).string());
2452 }
2453 fprintf(stdout, "\n");
2454 // !!! TODO: throw an error here in some circumstances
2455 }
2456
2457 // Check that all requested localizations are present for this string
2458 if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
2459 const char* allConfigs = mBundle->getConfigurations();
2460 const char* start = allConfigs;
2461 const char* comma;
2462
2463 do {
2464 String8 config;
2465 comma = strchr(start, ',');
2466 if (comma != NULL) {
2467 config.setTo(start, comma - start);
2468 start = comma + 1;
2469 } else {
2470 config.setTo(start);
2471 }
2472
2473 // don't bother with the pseudolocale "zz_ZZ"
2474 if (config != "zz_ZZ") {
2475 if (configSet.find(config) == configSet.end()) {
2476 // okay, no specific localization found. it's possible that we are
2477 // requiring a specific regional localization [e.g. de_DE] but there is an
2478 // available string in the generic language localization [e.g. de];
2479 // consider that string to have fulfilled the localization requirement.
2480 String8 region(config.string(), 2);
2481 if (configSet.find(region) == configSet.end()) {
2482 if (configSet.count(defaultLocale) == 0) {
1782ef54 2483 fprintf(stdout, "aapt: warning: "
571313a5 2484 "**** string '%s' has no default or required localization "
a534180c
TAOSP
2485 "for '%s' in %s\n",
2486 String8(nameIter->first).string(),
2487 config.string(),
2488 mBundle->getResourceSourceDirs()[0]);
a534180c
TAOSP
2489 }
2490 }
2491 }
2492 }
2493 } while (comma != NULL);
2494 }
2495 }
2496
2497 return err;
2498}
2499
2500
2501status_t
2502ResourceFilter::parse(const char* arg)
2503{
2504 if (arg == NULL) {
2505 return 0;
2506 }
2507
2508 const char* p = arg;
2509 const char* q;
2510
2511 while (true) {
2512 q = strchr(p, ',');
2513 if (q == NULL) {
2514 q = p + strlen(p);
2515 }
2516
2517 String8 part(p, q-p);
2518
2519 if (part == "zz_ZZ") {
2520 mContainsPseudo = true;
2521 }
2522 int axis;
2523 uint32_t value;
2524 if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
2525 fprintf(stderr, "Invalid configuration: %s\n", arg);
2526 fprintf(stderr, " ");
2527 for (int i=0; i<p-arg; i++) {
2528 fprintf(stderr, " ");
2529 }
2530 for (int i=0; i<q-p; i++) {
2531 fprintf(stderr, "^");
2532 }
2533 fprintf(stderr, "\n");
2534 return 1;
2535 }
2536
2537 ssize_t index = mData.indexOfKey(axis);
2538 if (index < 0) {
2539 mData.add(axis, SortedVector<uint32_t>());
2540 }
2541 SortedVector<uint32_t>& sv = mData.editValueFor(axis);
2542 sv.add(value);
2543 // if it's a locale with a region, also match an unmodified locale of the
2544 // same language
2545 if (axis == AXIS_LANGUAGE) {
2546 if (value & 0xffff0000) {
2547 sv.add(value & 0x0000ffff);
2548 }
2549 }
2550 p = q;
2551 if (!*p) break;
2552 p++;
2553 }
2554
2555 return NO_ERROR;
2556}
2557
2558bool
460ed1b2 2559ResourceFilter::match(int axis, uint32_t value) const
a534180c
TAOSP
2560{
2561 if (value == 0) {
2562 // they didn't specify anything so take everything
2563 return true;
2564 }
2565 ssize_t index = mData.indexOfKey(axis);
2566 if (index < 0) {
2567 // we didn't request anything on this axis so take everything
2568 return true;
2569 }
2570 const SortedVector<uint32_t>& sv = mData.valueAt(index);
2571 return sv.indexOf(value) >= 0;
2572}
2573
2574bool
460ed1b2 2575ResourceFilter::match(const ResTable_config& config) const
a534180c
TAOSP
2576{
2577 if (config.locale) {
2578 uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
2579 | (config.language[1] << 8) | (config.language[0]);
2580 if (!match(AXIS_LANGUAGE, locale)) {
2581 return false;
2582 }
2583 }
2584 if (!match(AXIS_ORIENTATION, config.orientation)) {
2585 return false;
2586 }
2ca01a37
TH
2587 if (!match(AXIS_UIMODETYPE, (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE))) {
2588 return false;
2589 }
2590 if (!match(AXIS_UIMODENIGHT, (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT))) {
2591 return false;
2592 }
a534180c
TAOSP
2593 if (!match(AXIS_DENSITY, config.density)) {
2594 return false;
2595 }
2596 if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
2597 return false;
2598 }
2599 if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
2600 return false;
2601 }
2602 if (!match(AXIS_KEYBOARD, config.keyboard)) {
2603 return false;
2604 }
2605 if (!match(AXIS_NAVIGATION, config.navigation)) {
2606 return false;
2607 }
2608 if (!match(AXIS_SCREENSIZE, config.screenSize)) {
2609 return false;
2610 }
f1f3915b
DH
2611 if (!match(AXIS_SMALLESTSCREENWIDTHDP, config.smallestScreenWidthDp)) {
2612 return false;
2613 }
4ee37df2
DH
2614 if (!match(AXIS_SCREENWIDTHDP, config.screenWidthDp)) {
2615 return false;
2616 }
2617 if (!match(AXIS_SCREENHEIGHTDP, config.screenHeightDp)) {
2618 return false;
2619 }
2620 if (!match(AXIS_SCREENLAYOUTSIZE, config.screenLayout&ResTable_config::MASK_SCREENSIZE)) {
2621 return false;
2622 }
a534180c
TAOSP
2623 if (!match(AXIS_VERSION, config.version)) {
2624 return false;
2625 }
2626 return true;
2627}
2628
2629status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
2630{
2631 ResourceFilter filter;
2632 status_t err = filter.parse(bundle->getConfigurations());
2633 if (err != NO_ERROR) {
2634 return err;
2635 }
2636
2637 const size_t N = mOrderedPackages.size();
2638 size_t pi;
2639
460ed1b2
KR
2640 const static String16 mipmap16("mipmap");
2641
1bad9d29 2642 bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
5af43148 2643
a534180c
TAOSP
2644 // Iterate through all data, collecting all values (strings,
2645 // references, etc).
5af43148 2646 StringPool valueStrings = StringPool(false, useUTF8);
a534180c
TAOSP
2647 for (pi=0; pi<N; pi++) {
2648 sp<Package> p = mOrderedPackages.itemAt(pi);
2649 if (p->getTypes().size() == 0) {
2650 // Empty, skip!
2651 continue;
2652 }
2653
5af43148
KR
2654 StringPool typeStrings = StringPool(false, useUTF8);
2655 StringPool keyStrings = StringPool(false, useUTF8);
a534180c
TAOSP
2656
2657 const size_t N = p->getOrderedTypes().size();
2658 for (size_t ti=0; ti<N; ti++) {
2659 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2660 if (t == NULL) {
2661 typeStrings.add(String16("<empty>"), false);
2662 continue;
2663 }
460ed1b2
KR
2664 const String16 typeName(t->getName());
2665 typeStrings.add(typeName, false);
2666
2667 const bool filterable = (typeName != mipmap16);
a534180c
TAOSP
2668
2669 const size_t N = t->getOrderedConfigs().size();
2670 for (size_t ci=0; ci<N; ci++) {
2671 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2672 if (c == NULL) {
2673 continue;
2674 }
2675 const size_t N = c->getEntries().size();
2676 for (size_t ei=0; ei<N; ei++) {
2677 ConfigDescription config = c->getEntries().keyAt(ei);
460ed1b2 2678 if (filterable && !filter.match(config)) {
a534180c
TAOSP
2679 continue;
2680 }
2681 sp<Entry> e = c->getEntries().valueAt(ei);
2682 if (e == NULL) {
2683 continue;
2684 }
2685 e->setNameIndex(keyStrings.add(e->getName(), true));
2686 status_t err = e->prepareFlatten(&valueStrings, this);
2687 if (err != NO_ERROR) {
2688 return err;
2689 }
2690 }
2691 }
2692 }
2693
2694 p->setTypeStrings(typeStrings.createStringBlock());
2695 p->setKeyStrings(keyStrings.createStringBlock());
2696 }
2697
2698 ssize_t strAmt = 0;
2699
2700 // Now build the array of package chunks.
2701 Vector<sp<AaptFile> > flatPackages;
2702 for (pi=0; pi<N; pi++) {
2703 sp<Package> p = mOrderedPackages.itemAt(pi);
2704 if (p->getTypes().size() == 0) {
2705 // Empty, skip!
2706 continue;
2707 }
2708
2709 const size_t N = p->getTypeStrings().size();
2710
2711 const size_t baseSize = sizeof(ResTable_package);
2712
2713 // Start the package data.
2714 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
2715 ResTable_package* header = (ResTable_package*)data->editData(baseSize);
2716 if (header == NULL) {
2717 fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
2718 return NO_MEMORY;
2719 }
2720 memset(header, 0, sizeof(*header));
2721 header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
2722 header->header.headerSize = htods(sizeof(*header));
2723 header->id = htodl(p->getAssignedId());
2724 strcpy16_htod(header->name, p->getName().string());
2725
2726 // Write the string blocks.
2727 const size_t typeStringsStart = data->getSize();
2728 sp<AaptFile> strFile = p->getTypeStringsData();
2729 ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
2730 #if PRINT_STRING_METRICS
2731 fprintf(stderr, "**** type strings: %d\n", amt);
2732 #endif
2733 strAmt += amt;
2734 if (amt < 0) {
2735 return amt;
2736 }
2737 const size_t keyStringsStart = data->getSize();
2738 strFile = p->getKeyStringsData();
2739 amt = data->writeData(strFile->getData(), strFile->getSize());
2740 #if PRINT_STRING_METRICS
2741 fprintf(stderr, "**** key strings: %d\n", amt);
2742 #endif
2743 strAmt += amt;
2744 if (amt < 0) {
2745 return amt;
2746 }
2747
2748 // Build the type chunks inside of this package.
2749 for (size_t ti=0; ti<N; ti++) {
2750 // Retrieve them in the same order as the type string block.
2751 size_t len;
2752 String16 typeName(p->getTypeStrings().stringAt(ti, &len));
2753 sp<Type> t = p->getTypes().valueFor(typeName);
2754 LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
2755 "Type name %s not found",
2756 String8(typeName).string());
2757
460ed1b2
KR
2758 const bool filterable = (typeName != mipmap16);
2759
a534180c
TAOSP
2760 const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
2761
2762 // First write the typeSpec chunk, containing information about
2763 // each resource entry in this type.
2764 {
2765 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
2766 const size_t typeSpecStart = data->getSize();
2767 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
2768 (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
2769 if (tsHeader == NULL) {
2770 fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
2771 return NO_MEMORY;
2772 }
2773 memset(tsHeader, 0, sizeof(*tsHeader));
2774 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
2775 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
2776 tsHeader->header.size = htodl(typeSpecSize);
2777 tsHeader->id = ti+1;
2778 tsHeader->entryCount = htodl(N);
2779
2780 uint32_t* typeSpecFlags = (uint32_t*)
2781 (((uint8_t*)data->editData())
2782 + typeSpecStart + sizeof(ResTable_typeSpec));
2783 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
460ed1b2 2784
a534180c
TAOSP
2785 for (size_t ei=0; ei<N; ei++) {
2786 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2787 if (cl->getPublic()) {
2788 typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
2789 }
2790 const size_t CN = cl->getEntries().size();
2791 for (size_t ci=0; ci<CN; ci++) {
460ed1b2 2792 if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
a534180c
TAOSP
2793 continue;
2794 }
2795 for (size_t cj=ci+1; cj<CN; cj++) {
460ed1b2 2796 if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
a534180c
TAOSP
2797 continue;
2798 }
2799 typeSpecFlags[ei] |= htodl(
2800 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
2801 }
2802 }
2803 }
2804 }
2805
2806 // We need to write one type chunk for each configuration for
2807 // which we have entries in this type.
2808 const size_t NC = t->getUniqueConfigs().size();
2809
2810 const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
2811
2812 for (size_t ci=0; ci<NC; ci++) {
2813 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
2814
2815 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
f1f3915b
DH
2816 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
2817 "sw%ddp w%ddp h%ddp\n",
a534180c
TAOSP
2818 ti+1,
2819 config.mcc, config.mnc,
2820 config.language[0] ? config.language[0] : '-',
2821 config.language[1] ? config.language[1] : '-',
2822 config.country[0] ? config.country[0] : '-',
2823 config.country[1] ? config.country[1] : '-',
2824 config.orientation,
2ca01a37 2825 config.uiMode,
a534180c
TAOSP
2826 config.touchscreen,
2827 config.density,
2828 config.keyboard,
2829 config.inputFlags,
2830 config.navigation,
2831 config.screenWidth,
4ee37df2 2832 config.screenHeight,
f1f3915b 2833 config.smallestScreenWidthDp,
4ee37df2
DH
2834 config.screenWidthDp,
2835 config.screenHeightDp));
a534180c 2836
460ed1b2 2837 if (filterable && !filter.match(config)) {
a534180c
TAOSP
2838 continue;
2839 }
2840
2841 const size_t typeStart = data->getSize();
2842
2843 ResTable_type* tHeader = (ResTable_type*)
2844 (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
2845 if (tHeader == NULL) {
2846 fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
2847 return NO_MEMORY;
2848 }
2849
2850 memset(tHeader, 0, sizeof(*tHeader));
2851 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
2852 tHeader->header.headerSize = htods(sizeof(*tHeader));
2853 tHeader->id = ti+1;
2854 tHeader->entryCount = htodl(N);
2855 tHeader->entriesStart = htodl(typeSize);
2856 tHeader->config = config;
2857 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
f1f3915b
DH
2858 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
2859 "sw%ddp w%ddp h%ddp\n",
a534180c
TAOSP
2860 ti+1,
2861 tHeader->config.mcc, tHeader->config.mnc,
2862 tHeader->config.language[0] ? tHeader->config.language[0] : '-',
2863 tHeader->config.language[1] ? tHeader->config.language[1] : '-',
2864 tHeader->config.country[0] ? tHeader->config.country[0] : '-',
2865 tHeader->config.country[1] ? tHeader->config.country[1] : '-',
2866 tHeader->config.orientation,
2ca01a37 2867 tHeader->config.uiMode,
a534180c
TAOSP
2868 tHeader->config.touchscreen,
2869 tHeader->config.density,
2870 tHeader->config.keyboard,
2871 tHeader->config.inputFlags,
2872 tHeader->config.navigation,
2873 tHeader->config.screenWidth,
4ee37df2 2874 tHeader->config.screenHeight,
f1f3915b 2875 tHeader->config.smallestScreenWidthDp,
4ee37df2
DH
2876 tHeader->config.screenWidthDp,
2877 tHeader->config.screenHeightDp));
a534180c
TAOSP
2878 tHeader->config.swapHtoD();
2879
2880 // Build the entries inside of this type.
2881 for (size_t ei=0; ei<N; ei++) {
2882 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2883 sp<Entry> e = cl->getEntries().valueFor(config);
2884
2885 // Set the offset for this entry in its type.
2886 uint32_t* index = (uint32_t*)
2887 (((uint8_t*)data->editData())
2888 + typeStart + sizeof(ResTable_type));
2889 if (e != NULL) {
2890 index[ei] = htodl(data->getSize()-typeStart-typeSize);
2891
2892 // Create the entry.
2893 ssize_t amt = e->flatten(bundle, data, cl->getPublic());
2894 if (amt < 0) {
2895 return amt;
2896 }
2897 } else {
2898 index[ei] = htodl(ResTable_type::NO_ENTRY);
2899 }
2900 }
2901
2902 // Fill in the rest of the type information.
2903 tHeader = (ResTable_type*)
2904 (((uint8_t*)data->editData()) + typeStart);
2905 tHeader->header.size = htodl(data->getSize()-typeStart);
2906 }
2907 }
2908
2909 // Fill in the rest of the package information.
2910 header = (ResTable_package*)data->editData();
2911 header->header.size = htodl(data->getSize());
2912 header->typeStrings = htodl(typeStringsStart);
2913 header->lastPublicType = htodl(p->getTypeStrings().size());
2914 header->keyStrings = htodl(keyStringsStart);
2915 header->lastPublicKey = htodl(p->getKeyStrings().size());
2916
2917 flatPackages.add(data);
2918 }
2919
2920 // And now write out the final chunks.
2921 const size_t dataStart = dest->getSize();
2922
2923 {
2924 // blah
2925 ResTable_header header;
2926 memset(&header, 0, sizeof(header));
2927 header.header.type = htods(RES_TABLE_TYPE);
2928 header.header.headerSize = htods(sizeof(header));
2929 header.packageCount = htodl(flatPackages.size());
2930 status_t err = dest->writeData(&header, sizeof(header));
2931 if (err != NO_ERROR) {
2932 fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
2933 return err;
2934 }
2935 }
2936
2937 ssize_t strStart = dest->getSize();
2938 err = valueStrings.writeStringBlock(dest);
2939 if (err != NO_ERROR) {
2940 return err;
2941 }
2942
2943 ssize_t amt = (dest->getSize()-strStart);
2944 strAmt += amt;
2945 #if PRINT_STRING_METRICS
2946 fprintf(stderr, "**** value strings: %d\n", amt);
2947 fprintf(stderr, "**** total strings: %d\n", strAmt);
2948 #endif
2949
2950 for (pi=0; pi<flatPackages.size(); pi++) {
2951 err = dest->writeData(flatPackages[pi]->getData(),
2952 flatPackages[pi]->getSize());
2953 if (err != NO_ERROR) {
2954 fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
2955 return err;
2956 }
2957 }
2958
2959 ResTable_header* header = (ResTable_header*)
2960 (((uint8_t*)dest->getData()) + dataStart);
2961 header->header.size = htodl(dest->getSize() - dataStart);
2962
2963 NOISY(aout << "Resource table:"
2964 << HexDump(dest->getData(), dest->getSize()) << endl);
2965
2966 #if PRINT_STRING_METRICS
2967 fprintf(stderr, "**** total resource table size: %d / %d%% strings\n",
2968 dest->getSize(), (strAmt*100)/dest->getSize());
2969 #endif
2970
2971 return NO_ERROR;
2972}
2973
2974void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
2975{
2976 fprintf(fp,
2977 "<!-- This file contains <public> resource definitions for all\n"
2978 " resources that were generated from the source data. -->\n"
2979 "\n"
2980 "<resources>\n");
2981
2982 writePublicDefinitions(package, fp, true);
2983 writePublicDefinitions(package, fp, false);
2984
2985 fprintf(fp,
2986 "\n"
2987 "</resources>\n");
2988}
2989
2990void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
2991{
2992 bool didHeader = false;
2993
2994 sp<Package> pkg = mPackages.valueFor(package);
2995 if (pkg != NULL) {
2996 const size_t NT = pkg->getOrderedTypes().size();
2997 for (size_t i=0; i<NT; i++) {
2998 sp<Type> t = pkg->getOrderedTypes().itemAt(i);
2999 if (t == NULL) {
3000 continue;
3001 }
3002
3003 bool didType = false;
3004
3005 const size_t NC = t->getOrderedConfigs().size();
3006 for (size_t j=0; j<NC; j++) {
3007 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
3008 if (c == NULL) {
3009 continue;
3010 }
3011
3012 if (c->getPublic() != pub) {
3013 continue;
3014 }
3015
3016 if (!didType) {
3017 fprintf(fp, "\n");
3018 didType = true;
3019 }
3020 if (!didHeader) {
3021 if (pub) {
3022 fprintf(fp," <!-- PUBLIC SECTION. These resources have been declared public.\n");
3023 fprintf(fp," Changes to these definitions will break binary compatibility. -->\n\n");
3024 } else {
3025 fprintf(fp," <!-- PRIVATE SECTION. These resources have not been declared public.\n");
3026 fprintf(fp," You can make them public my moving these lines into a file in res/values. -->\n\n");
3027 }
3028 didHeader = true;
3029 }
3030 if (!pub) {
3031 const size_t NE = c->getEntries().size();
3032 for (size_t k=0; k<NE; k++) {
3033 const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
3034 if (pos.file != "") {
3035 fprintf(fp," <!-- Declared at %s:%d -->\n",
3036 pos.file.string(), pos.line);
3037 }
3038 }
3039 }
3040 fprintf(fp, " <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
3041 String8(t->getName()).string(),
3042 String8(c->getName()).string(),
3043 getResId(pkg, t, c->getEntryIndex()));
3044 }
3045 }
3046 }
3047}
3048
3049ResourceTable::Item::Item(const SourcePos& _sourcePos,
3050 bool _isId,
3051 const String16& _value,
3052 const Vector<StringPool::entry_style_span>* _style,
3053 int32_t _format)
3054 : sourcePos(_sourcePos)
3055 , isId(_isId)
3056 , value(_value)
3057 , format(_format)
3058 , bagKeyId(0)
3059 , evaluating(false)
3060{
3061 if (_style) {
3062 style = *_style;
3063 }
3064}
3065
3066status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
3067{
3068 if (mType == TYPE_BAG) {
3069 return NO_ERROR;
3070 }
3071 if (mType == TYPE_UNKNOWN) {
3072 mType = TYPE_BAG;
3073 return NO_ERROR;
3074 }
3075 sourcePos.error("Resource entry %s is already defined as a single item.\n"
3076 "%s:%d: Originally defined here.\n",
3077 String8(mName).string(),
3078 mItem.sourcePos.file.string(), mItem.sourcePos.line);
3079 return UNKNOWN_ERROR;
3080}
3081
3082status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
3083 const String16& value,
3084 const Vector<StringPool::entry_style_span>* style,
3085 int32_t format,
3086 const bool overwrite)
3087{
3088 Item item(sourcePos, false, value, style);
3089
3090 if (mType == TYPE_BAG) {
3091 const Item& item(mBag.valueAt(0));
3092 sourcePos.error("Resource entry %s is already defined as a bag.\n"
3093 "%s:%d: Originally defined here.\n",
3094 String8(mName).string(),
3095 item.sourcePos.file.string(), item.sourcePos.line);
3096 return UNKNOWN_ERROR;
3097 }
3098 if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
3099 sourcePos.error("Resource entry %s is already defined.\n"
3100 "%s:%d: Originally defined here.\n",
3101 String8(mName).string(),
3102 mItem.sourcePos.file.string(), mItem.sourcePos.line);
3103 return UNKNOWN_ERROR;
3104 }
6c9f72e4 3105
a534180c
TAOSP
3106 mType = TYPE_ITEM;
3107 mItem = item;
3108 mItemFormat = format;
3109 return NO_ERROR;
3110}
3111
3112status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
3113 const String16& key, const String16& value,
3114 const Vector<StringPool::entry_style_span>* style,
3115 bool replace, bool isId, int32_t format)
3116{
3117 status_t err = makeItABag(sourcePos);
3118 if (err != NO_ERROR) {
3119 return err;
3120 }
3121
3122 Item item(sourcePos, isId, value, style, format);
3123
3124 // XXX NOTE: there is an error if you try to have a bag with two keys,
3125 // one an attr and one an id, with the same name. Not something we
3126 // currently ever have to worry about.
3127 ssize_t origKey = mBag.indexOfKey(key);
3128 if (origKey >= 0) {
3129 if (!replace) {
3130 const Item& item(mBag.valueAt(origKey));
3131 sourcePos.error("Resource entry %s already has bag item %s.\n"
3132 "%s:%d: Originally defined here.\n",
3133 String8(mName).string(), String8(key).string(),
3134 item.sourcePos.file.string(), item.sourcePos.line);
3135 return UNKNOWN_ERROR;
3136 }
3137 //printf("Replacing %s with %s\n",
3138 // String8(mBag.valueFor(key).value).string(), String8(value).string());
3139 mBag.replaceValueFor(key, item);
3140 }
3141
3142 mBag.add(key, item);
3143 return NO_ERROR;
3144}
3145
76486813
RG
3146status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
3147{
3148 status_t err = makeItABag(sourcePos);
3149 if (err != NO_ERROR) {
3150 return err;
3151 }
3152
3153 mBag.clear();
3154 return NO_ERROR;
3155}
3156
a534180c
TAOSP
3157status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
3158 const String16& package)
3159{
3160 const String16 attr16("attr");
3161 const String16 id16("id");
3162 const size_t N = mBag.size();
3163 for (size_t i=0; i<N; i++) {
3164 const String16& key = mBag.keyAt(i);
3165 const Item& it = mBag.valueAt(i);
3166 if (it.isId) {
3167 if (!table->hasBagOrEntry(key, &id16, &package)) {
3168 String16 value("false");
3169 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
3170 id16, key, value);
3171 if (err != NO_ERROR) {
3172 return err;
3173 }
3174 }
3175 } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
3176
3177#if 1
3178// fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
3179// String8(key).string());
3180// const Item& item(mBag.valueAt(i));
3181// fprintf(stderr, "Referenced from file %s line %d\n",
3182// item.sourcePos.file.string(), item.sourcePos.line);
3183// return UNKNOWN_ERROR;
3184#else
3185 char numberStr[16];
3186 sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
3187 status_t err = table->addBag(SourcePos("<generated>", 0), package,
3188 attr16, key, String16(""),
3189 String16("^type"),
3190 String16(numberStr), NULL, NULL);
3191 if (err != NO_ERROR) {
3192 return err;
3193 }
3194#endif
3195 }
3196 }
3197 return NO_ERROR;
3198}
3199
3200status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
3201 const String16& package)
3202{
3203 bool hasErrors = false;
3204
3205 if (mType == TYPE_BAG) {
3206 const char* errorMsg;
3207 const String16 style16("style");
3208 const String16 attr16("attr");
3209 const String16 id16("id");
3210 mParentId = 0;
3211 if (mParent.size() > 0) {
16af5a27 3212 mParentId = table->getResId(mParent, &style16, NULL, &errorMsg, false);
a534180c
TAOSP
3213 if (mParentId == 0) {
3214 mPos.error("Error retrieving parent for item: %s '%s'.\n",
3215 errorMsg, String8(mParent).string());
3216 hasErrors = true;
3217 }
3218 }
3219 const size_t N = mBag.size();
3220 for (size_t i=0; i<N; i++) {
3221 const String16& key = mBag.keyAt(i);
3222 Item& it = mBag.editValueAt(i);
3223 it.bagKeyId = table->getResId(key,
16af5a27 3224 it.isId ? &id16 : &attr16, NULL, &errorMsg, false);
a534180c
TAOSP
3225 //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
3226 if (it.bagKeyId == 0) {
3227 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
3228 String8(it.isId ? id16 : attr16).string(),
3229 String8(key).string());
3230 hasErrors = true;
3231 }
3232 }
3233 }
3234 return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
3235}
3236
3237status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
3238{
3239 if (mType == TYPE_ITEM) {
3240 Item& it = mItem;
3241 AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
3242 if (!table->stringToValue(&it.parsedValue, strings,
3243 it.value, false, true, 0,
3244 &it.style, NULL, &ac, mItemFormat)) {
3245 return UNKNOWN_ERROR;
3246 }
3247 } else if (mType == TYPE_BAG) {
3248 const size_t N = mBag.size();
3249 for (size_t i=0; i<N; i++) {
3250 const String16& key = mBag.keyAt(i);
3251 Item& it = mBag.editValueAt(i);
3252 AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
3253 if (!table->stringToValue(&it.parsedValue, strings,
3254 it.value, false, true, it.bagKeyId,
3255 &it.style, NULL, &ac, it.format)) {
3256 return UNKNOWN_ERROR;
3257 }
3258 }
3259 } else {
3260 mPos.error("Error: entry %s is not a single item or a bag.\n",
3261 String8(mName).string());
3262 return UNKNOWN_ERROR;
3263 }
3264 return NO_ERROR;
3265}
3266
3267ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
3268{
3269 size_t amt = 0;
3270 ResTable_entry header;
3271 memset(&header, 0, sizeof(header));
3272 header.size = htods(sizeof(header));
3273 const type ty = this != NULL ? mType : TYPE_ITEM;
3274 if (this != NULL) {
3275 if (ty == TYPE_BAG) {
3276 header.flags |= htods(header.FLAG_COMPLEX);
3277 }
3278 if (isPublic) {
3279 header.flags |= htods(header.FLAG_PUBLIC);
3280 }
3281 header.key.index = htodl(mNameIndex);
3282 }
3283 if (ty != TYPE_BAG) {
3284 status_t err = data->writeData(&header, sizeof(header));
3285 if (err != NO_ERROR) {
3286 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3287 return err;
3288 }
3289
3290 const Item& it = mItem;
3291 Res_value par;
3292 memset(&par, 0, sizeof(par));
3293 par.size = htods(it.parsedValue.size);
3294 par.dataType = it.parsedValue.dataType;
3295 par.res0 = it.parsedValue.res0;
3296 par.data = htodl(it.parsedValue.data);
3297 #if 0
3298 printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
3299 String8(mName).string(), it.parsedValue.dataType,
3300 it.parsedValue.data, par.res0);
3301 #endif
3302 err = data->writeData(&par, it.parsedValue.size);
3303 if (err != NO_ERROR) {
3304 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3305 return err;
3306 }
3307 amt += it.parsedValue.size;
3308 } else {
3309 size_t N = mBag.size();
3310 size_t i;
3311 // Create correct ordering of items.
3312 KeyedVector<uint32_t, const Item*> items;
3313 for (i=0; i<N; i++) {
3314 const Item& it = mBag.valueAt(i);
3315 items.add(it.bagKeyId, &it);
3316 }
3317 N = items.size();
3318
3319 ResTable_map_entry mapHeader;
3320 memcpy(&mapHeader, &header, sizeof(header));
3321 mapHeader.size = htods(sizeof(mapHeader));
3322 mapHeader.parent.ident = htodl(mParentId);
3323 mapHeader.count = htodl(N);
3324 status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
3325 if (err != NO_ERROR) {
3326 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3327 return err;
3328 }
3329
3330 for (i=0; i<N; i++) {
3331 const Item& it = *items.valueAt(i);
3332 ResTable_map map;
3333 map.name.ident = htodl(it.bagKeyId);
3334 map.value.size = htods(it.parsedValue.size);
3335 map.value.dataType = it.parsedValue.dataType;
3336 map.value.res0 = it.parsedValue.res0;
3337 map.value.data = htodl(it.parsedValue.data);
3338 err = data->writeData(&map, sizeof(map));
3339 if (err != NO_ERROR) {
3340 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3341 return err;
3342 }
3343 amt += sizeof(map);
3344 }
3345 }
3346 return amt;
3347}
3348
3349void ResourceTable::ConfigList::appendComment(const String16& comment,
3350 bool onlyIfEmpty)
3351{
3352 if (comment.size() <= 0) {
3353 return;
3354 }
3355 if (onlyIfEmpty && mComment.size() > 0) {
3356 return;
3357 }
3358 if (mComment.size() > 0) {
3359 mComment.append(String16("\n"));
3360 }
3361 mComment.append(comment);
3362}
3363
3364void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
3365{
3366 if (comment.size() <= 0) {
3367 return;
3368 }
3369 if (mTypeComment.size() > 0) {
3370 mTypeComment.append(String16("\n"));
3371 }
3372 mTypeComment.append(comment);
3373}
3374
3375status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
3376 const String16& name,
3377 const uint32_t ident)
3378{
3379 #if 0
3380 int32_t entryIdx = Res_GETENTRY(ident);
3381 if (entryIdx < 0) {
3382 sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
3383 String8(mName).string(), String8(name).string(), ident);
3384 return UNKNOWN_ERROR;
3385 }
3386 #endif
3387
3388 int32_t typeIdx = Res_GETTYPE(ident);
3389 if (typeIdx >= 0) {
3390 typeIdx++;
3391 if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
3392 sourcePos.error("Public resource %s/%s has conflicting type codes for its"
3393 " public identifiers (0x%x vs 0x%x).\n",
3394 String8(mName).string(), String8(name).string(),
3395 mPublicIndex, typeIdx);
3396 return UNKNOWN_ERROR;
3397 }
3398 mPublicIndex = typeIdx;
3399 }
3400
3401 if (mFirstPublicSourcePos == NULL) {
3402 mFirstPublicSourcePos = new SourcePos(sourcePos);
3403 }
3404
3405 if (mPublic.indexOfKey(name) < 0) {
3406 mPublic.add(name, Public(sourcePos, String16(), ident));
3407 } else {
3408 Public& p = mPublic.editValueFor(name);
3409 if (p.ident != ident) {
3410 sourcePos.error("Public resource %s/%s has conflicting public identifiers"
3411 " (0x%08x vs 0x%08x).\n"
3412 "%s:%d: Originally defined here.\n",
3413 String8(mName).string(), String8(name).string(), p.ident, ident,
3414 p.sourcePos.file.string(), p.sourcePos.line);
3415 return UNKNOWN_ERROR;
3416 }
3417 }
3418
3419 return NO_ERROR;
3420}
3421
a8c9cd56
DH
3422void ResourceTable::Type::canAddEntry(const String16& name)
3423{
3424 mCanAddEntries.add(name);
3425}
3426
a534180c
TAOSP
3427sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
3428 const SourcePos& sourcePos,
3429 const ResTable_config* config,
6c9f72e4 3430 bool doSetIndex,
636d3b70
XD
3431 bool overlay,
3432 bool autoAddOverlay)
a534180c
TAOSP
3433{
3434 int pos = -1;
3435 sp<ConfigList> c = mConfigs.valueFor(entry);
3436 if (c == NULL) {
636d3b70 3437 if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
a8c9cd56
DH
3438 sourcePos.error("Resource at %s appears in overlay but not"
3439 " in the base package; use <add-resource> to add.\n",
3440 String8(entry).string());
6c9f72e4
RG
3441 return NULL;
3442 }
a534180c
TAOSP
3443 c = new ConfigList(entry, sourcePos);
3444 mConfigs.add(entry, c);
3445 pos = (int)mOrderedConfigs.size();
3446 mOrderedConfigs.add(c);
3447 if (doSetIndex) {
3448 c->setEntryIndex(pos);
3449 }
3450 }
3451
3452 ConfigDescription cdesc;
3453 if (config) cdesc = *config;
3454
3455 sp<Entry> e = c->getEntries().valueFor(cdesc);
3456 if (e == NULL) {
3457 if (config != NULL) {
3458 NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
f1f3915b
DH
3459 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
3460 "sw%ddp w%ddp h%ddp\n",
a534180c
TAOSP
3461 sourcePos.file.string(), sourcePos.line,
3462 config->mcc, config->mnc,
3463 config->language[0] ? config->language[0] : '-',
3464 config->language[1] ? config->language[1] : '-',
3465 config->country[0] ? config->country[0] : '-',
3466 config->country[1] ? config->country[1] : '-',
3467 config->orientation,
3468 config->touchscreen,
3469 config->density,
3470 config->keyboard,
3471 config->inputFlags,
3472 config->navigation,
3473 config->screenWidth,
4ee37df2 3474 config->screenHeight,
f1f3915b 3475 config->smallestScreenWidthDp,
4ee37df2
DH
3476 config->screenWidthDp,
3477 config->screenHeightDp));
a534180c
TAOSP
3478 } else {
3479 NOISY(printf("New entry at %s:%d: NULL config\n",
3480 sourcePos.file.string(), sourcePos.line));
3481 }
3482 e = new Entry(entry, sourcePos);
3483 c->addEntry(cdesc, e);
3484 /*
3485 if (doSetIndex) {
3486 if (pos < 0) {
3487 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
3488 if (mOrderedConfigs[pos] == c) {
3489 break;
3490 }
3491 }
3492 if (pos >= (int)mOrderedConfigs.size()) {
3493 sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
3494 return NULL;
3495 }
3496 }
3497 e->setEntryIndex(pos);
3498 }
3499 */
3500 }
3501
3502 mUniqueConfigs.add(cdesc);
3503
3504 return e;
3505}
3506
3507status_t ResourceTable::Type::applyPublicEntryOrder()
3508{
3509 size_t N = mOrderedConfigs.size();
3510 Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
3511 bool hasError = false;
3512
3513 size_t i;
3514 for (i=0; i<N; i++) {
3515 mOrderedConfigs.replaceAt(NULL, i);
3516 }
3517
3518 const size_t NP = mPublic.size();
3519 //printf("Ordering %d configs from %d public defs\n", N, NP);
3520 size_t j;
3521 for (j=0; j<NP; j++) {
3522 const String16& name = mPublic.keyAt(j);
3523 const Public& p = mPublic.valueAt(j);
3524 int32_t idx = Res_GETENTRY(p.ident);
3525 //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
3526 // String8(mName).string(), String8(name).string(), p.ident, N);
3527 bool found = false;
3528 for (i=0; i<N; i++) {
3529 sp<ConfigList> e = origOrder.itemAt(i);
3530 //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
3531 if (e->getName() == name) {
3532 if (idx >= (int32_t)mOrderedConfigs.size()) {
3533 p.sourcePos.error("Public entry identifier 0x%x entry index "
3534 "is larger than available symbols (index %d, total symbols %d).\n",
3535 p.ident, idx, mOrderedConfigs.size());
3536 hasError = true;
3537 } else if (mOrderedConfigs.itemAt(idx) == NULL) {
3538 e->setPublic(true);
3539 e->setPublicSourcePos(p.sourcePos);
3540 mOrderedConfigs.replaceAt(e, idx);
3541 origOrder.removeAt(i);
3542 N--;
3543 found = true;
3544 break;
3545 } else {
3546 sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
3547
3548 p.sourcePos.error("Multiple entry names declared for public entry"
3549 " identifier 0x%x in type %s (%s vs %s).\n"
3550 "%s:%d: Originally defined here.",
3551 idx+1, String8(mName).string(),
3552 String8(oe->getName()).string(),
3553 String8(name).string(),
3554 oe->getPublicSourcePos().file.string(),
3555 oe->getPublicSourcePos().line);
3556 hasError = true;
3557 }
3558 }
3559 }
3560
3561 if (!found) {
3562 p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
3563 String8(mName).string(), String8(name).string());
3564 hasError = true;
3565 }
3566 }
3567
3568 //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
3569
3570 if (N != origOrder.size()) {
3571 printf("Internal error: remaining private symbol count mismatch\n");
3572 N = origOrder.size();
3573 }
3574
3575 j = 0;
3576 for (i=0; i<N; i++) {
3577 sp<ConfigList> e = origOrder.itemAt(i);
3578 // There will always be enough room for the remaining entries.
3579 while (mOrderedConfigs.itemAt(j) != NULL) {
3580 j++;
3581 }
3582 mOrderedConfigs.replaceAt(e, j);
3583 j++;
3584 }
3585
3586 return hasError ? UNKNOWN_ERROR : NO_ERROR;
3587}
3588
3589ResourceTable::Package::Package(const String16& name, ssize_t includedId)
3590 : mName(name), mIncludedId(includedId),
3591 mTypeStringsMapping(0xffffffff),
3592 mKeyStringsMapping(0xffffffff)
3593{
3594}
3595
3596sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
3597 const SourcePos& sourcePos,
3598 bool doSetIndex)
3599{
3600 sp<Type> t = mTypes.valueFor(type);
3601 if (t == NULL) {
3602 t = new Type(type, sourcePos);
3603 mTypes.add(type, t);
3604 mOrderedTypes.add(t);
3605 if (doSetIndex) {
3606 // For some reason the type's index is set to one plus the index
3607 // in the mOrderedTypes list, rather than just the index.
3608 t->setIndex(mOrderedTypes.size());
3609 }
3610 }
3611 return t;
3612}
3613
3614status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
3615{
3616 mTypeStringsData = data;
3617 status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
3618 if (err != NO_ERROR) {
3619 fprintf(stderr, "ERROR: Type string data is corrupt!\n");
3620 }
3621 return err;
3622}
3623
3624status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
3625{
3626 mKeyStringsData = data;
3627 status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
3628 if (err != NO_ERROR) {
3629 fprintf(stderr, "ERROR: Key string data is corrupt!\n");
3630 }
3631 return err;
3632}
3633
3634status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
3635 ResStringPool* strings,
3636 DefaultKeyedVector<String16, uint32_t>* mappings)
3637{
3638 if (data->getData() == NULL) {
3639 return UNKNOWN_ERROR;
3640 }
3641
3642 NOISY(aout << "Setting restable string pool: "
3643 << HexDump(data->getData(), data->getSize()) << endl);
3644
3645 status_t err = strings->setTo(data->getData(), data->getSize());
3646 if (err == NO_ERROR) {
3647 const size_t N = strings->size();
3648 for (size_t i=0; i<N; i++) {
3649 size_t len;
3650 mappings->add(String16(strings->stringAt(i, &len)), i);
3651 }
3652 }
3653 return err;
3654}
3655
3656status_t ResourceTable::Package::applyPublicTypeOrder()
3657{
3658 size_t N = mOrderedTypes.size();
3659 Vector<sp<Type> > origOrder(mOrderedTypes);
3660
3661 size_t i;
3662 for (i=0; i<N; i++) {
3663 mOrderedTypes.replaceAt(NULL, i);
3664 }
3665
3666 for (i=0; i<N; i++) {
3667 sp<Type> t = origOrder.itemAt(i);
3668 int32_t idx = t->getPublicIndex();
3669 if (idx > 0) {
3670 idx--;
3671 while (idx >= (int32_t)mOrderedTypes.size()) {
3672 mOrderedTypes.add();
3673 }
3674 if (mOrderedTypes.itemAt(idx) != NULL) {
3675 sp<Type> ot = mOrderedTypes.itemAt(idx);
3676 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
3677 " identifier 0x%x (%s vs %s).\n"
3678 "%s:%d: Originally defined here.",
3679 idx, String8(ot->getName()).string(),
3680 String8(t->getName()).string(),
3681 ot->getFirstPublicSourcePos().file.string(),
3682 ot->getFirstPublicSourcePos().line);
3683 return UNKNOWN_ERROR;
3684 }
3685 mOrderedTypes.replaceAt(t, idx);
3686 origOrder.removeAt(i);
3687 i--;
3688 N--;
3689 }
3690 }
3691
3692 size_t j=0;
3693 for (i=0; i<N; i++) {
3694 sp<Type> t = origOrder.itemAt(i);
3695 // There will always be enough room for the remaining types.
3696 while (mOrderedTypes.itemAt(j) != NULL) {
3697 j++;
3698 }
3699 mOrderedTypes.replaceAt(t, j);
3700 }
3701
3702 return NO_ERROR;
3703}
3704
3705sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
3706{
3707 sp<Package> p = mPackages.valueFor(package);
3708 if (p == NULL) {
b1bccba1
MK
3709 if (mBundle->getIsOverlayPackage()) {
3710 p = new Package(package, 0x00);
3711 } else if (mIsAppPackage) {
a534180c
TAOSP
3712 if (mHaveAppPackage) {
3713 fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
3714 "Use -x to create extended resources.\n");
3715 return NULL;
3716 }
3717 mHaveAppPackage = true;
3718 p = new Package(package, 127);
3719 } else {
3720 p = new Package(package, mNextPackageId);
3721 }
3722 //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
3723 // String8(package).string(), p->getAssignedId());
3724 mPackages.add(package, p);
3725 mOrderedPackages.add(p);
3726 mNextPackageId++;
3727 }
3728 return p;
3729}
3730
3731sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
3732 const String16& type,
3733 const SourcePos& sourcePos,
3734 bool doSetIndex)
3735{
3736 sp<Package> p = getPackage(package);
3737 if (p == NULL) {
3738 return NULL;
3739 }
3740 return p->getType(type, sourcePos, doSetIndex);
3741}
3742
3743sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
3744 const String16& type,
3745 const String16& name,
3746 const SourcePos& sourcePos,
6c9f72e4 3747 bool overlay,
a534180c
TAOSP
3748 const ResTable_config* config,
3749 bool doSetIndex)
3750{
3751 sp<Type> t = getType(package, type, sourcePos, doSetIndex);
3752 if (t == NULL) {
3753 return NULL;
3754 }
636d3b70 3755 return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
a534180c
TAOSP
3756}
3757
3758sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
3759 const ResTable_config* config) const
3760{
3761 int pid = Res_GETPACKAGE(resID)+1;
3762 const size_t N = mOrderedPackages.size();
3763 size_t i;
3764 sp<Package> p;
3765 for (i=0; i<N; i++) {
3766 sp<Package> check = mOrderedPackages[i];
3767 if (check->getAssignedId() == pid) {
3768 p = check;
3769 break;
3770 }
3771
3772 }
3773 if (p == NULL) {
406a85e3 3774 fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
a534180c
TAOSP
3775 return NULL;
3776 }
3777
3778 int tid = Res_GETTYPE(resID);
3779 if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
406a85e3 3780 fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
a534180c
TAOSP
3781 return NULL;
3782 }
3783 sp<Type> t = p->getOrderedTypes()[tid];
3784
3785 int eid = Res_GETENTRY(resID);
3786 if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
406a85e3 3787 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
a534180c
TAOSP
3788 return NULL;
3789 }
3790
3791 sp<ConfigList> c = t->getOrderedConfigs()[eid];
3792 if (c == NULL) {
406a85e3 3793 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
a534180c
TAOSP
3794 return NULL;
3795 }
3796
3797 ConfigDescription cdesc;
3798 if (config) cdesc = *config;
3799 sp<Entry> e = c->getEntries().valueFor(cdesc);
3800 if (c == NULL) {
406a85e3 3801 fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
a534180c
TAOSP
3802 return NULL;
3803 }
3804
3805 return e;
3806}
3807
3808const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
3809{
3810 sp<const Entry> e = getEntry(resID);
3811 if (e == NULL) {
3812 return NULL;
3813 }
3814
3815 const size_t N = e->getBag().size();
3816 for (size_t i=0; i<N; i++) {
3817 const Item& it = e->getBag().valueAt(i);
3818 if (it.bagKeyId == 0) {
406a85e3 3819 fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
a534180c
TAOSP
3820 String8(e->getName()).string(),
3821 String8(e->getBag().keyAt(i)).string());
3822 }
3823 if (it.bagKeyId == attrID) {
3824 return &it;
3825 }
3826 }
3827
3828 return NULL;
3829}
3830
3831bool ResourceTable::getItemValue(
3832 uint32_t resID, uint32_t attrID, Res_value* outValue)
3833{
3834 const Item* item = getItem(resID, attrID);
3835
3836 bool res = false;
3837 if (item != NULL) {
3838 if (item->evaluating) {
3839 sp<const Entry> e = getEntry(resID);
3840 const size_t N = e->getBag().size();
3841 size_t i;
3842 for (i=0; i<N; i++) {
3843 if (&e->getBag().valueAt(i) == item) {
3844 break;
3845 }
3846 }
406a85e3 3847 fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
a534180c
TAOSP
3848 String8(e->getName()).string(),
3849 String8(e->getBag().keyAt(i)).string());
3850 return false;
3851 }
3852 item->evaluating = true;
3853 res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
3854 NOISY(
3855 if (res) {
3856 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
3857 resID, attrID, String8(getEntry(resID)->getName()).string(),
3858 outValue->dataType, outValue->data);
3859 } else {
3860 printf("getItemValue of #%08x[#%08x]: failed\n",
3861 resID, attrID);
3862 }
3863 );
3864 item->evaluating = false;
3865 }
3866 return res;
3867}