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