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