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