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