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