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