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