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