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