]> git.saurik.com Git - android/aapt.git/blame - ResourceTable.cpp
am 51ce1607: am 46cc524a: (-s ours) am f7243dcc: Add --error-on-failed-insert option...
[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 2813 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
c3204d6e 2814 "sw%ddp w%ddp h%ddp dir:%d\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 2831 config.screenWidthDp,
c3204d6e
FDM
2832 config.screenHeightDp,
2833 config.layoutDirection));
a534180c 2834
460ed1b2 2835 if (filterable && !filter.match(config)) {
a534180c
TAOSP
2836 continue;
2837 }
2838
2839 const size_t typeStart = data->getSize();
2840
2841 ResTable_type* tHeader = (ResTable_type*)
2842 (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
2843 if (tHeader == NULL) {
2844 fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
2845 return NO_MEMORY;
2846 }
2847
2848 memset(tHeader, 0, sizeof(*tHeader));
2849 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
2850 tHeader->header.headerSize = htods(sizeof(*tHeader));
2851 tHeader->id = ti+1;
2852 tHeader->entryCount = htodl(N);
2853 tHeader->entriesStart = htodl(typeSize);
2854 tHeader->config = config;
2855 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
f1f3915b 2856 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
c3204d6e 2857 "sw%ddp w%ddp h%ddp dir:%d\n",
a534180c
TAOSP
2858 ti+1,
2859 tHeader->config.mcc, tHeader->config.mnc,
2860 tHeader->config.language[0] ? tHeader->config.language[0] : '-',
2861 tHeader->config.language[1] ? tHeader->config.language[1] : '-',
2862 tHeader->config.country[0] ? tHeader->config.country[0] : '-',
2863 tHeader->config.country[1] ? tHeader->config.country[1] : '-',
2864 tHeader->config.orientation,
2ca01a37 2865 tHeader->config.uiMode,
a534180c
TAOSP
2866 tHeader->config.touchscreen,
2867 tHeader->config.density,
2868 tHeader->config.keyboard,
2869 tHeader->config.inputFlags,
2870 tHeader->config.navigation,
2871 tHeader->config.screenWidth,
4ee37df2 2872 tHeader->config.screenHeight,
f1f3915b 2873 tHeader->config.smallestScreenWidthDp,
4ee37df2 2874 tHeader->config.screenWidthDp,
c3204d6e
FDM
2875 tHeader->config.screenHeightDp,
2876 tHeader->config.layoutDirection));
a534180c
TAOSP
2877 tHeader->config.swapHtoD();
2878
2879 // Build the entries inside of this type.
2880 for (size_t ei=0; ei<N; ei++) {
2881 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2882 sp<Entry> e = cl->getEntries().valueFor(config);
2883
2884 // Set the offset for this entry in its type.
2885 uint32_t* index = (uint32_t*)
2886 (((uint8_t*)data->editData())
2887 + typeStart + sizeof(ResTable_type));
2888 if (e != NULL) {
2889 index[ei] = htodl(data->getSize()-typeStart-typeSize);
2890
2891 // Create the entry.
2892 ssize_t amt = e->flatten(bundle, data, cl->getPublic());
2893 if (amt < 0) {
2894 return amt;
2895 }
11c3a26a 2896 validResources.editItemAt(ei) = true;
a534180c
TAOSP
2897 } else {
2898 index[ei] = htodl(ResTable_type::NO_ENTRY);
2899 }
2900 }
2901
2902 // Fill in the rest of the type information.
2903 tHeader = (ResTable_type*)
2904 (((uint8_t*)data->editData()) + typeStart);
2905 tHeader->header.size = htodl(data->getSize()-typeStart);
2906 }
11c3a26a
MK
2907
2908 for (size_t i = 0; i < N; ++i) {
2909 if (!validResources[i]) {
2910 sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
2911 fprintf(stderr, "warning: no entries written for %s/%s\n",
2912 String8(typeName).string(), String8(c->getName()).string());
2913 }
2914 }
a534180c
TAOSP
2915 }
2916
2917 // Fill in the rest of the package information.
2918 header = (ResTable_package*)data->editData();
2919 header->header.size = htodl(data->getSize());
2920 header->typeStrings = htodl(typeStringsStart);
2921 header->lastPublicType = htodl(p->getTypeStrings().size());
2922 header->keyStrings = htodl(keyStringsStart);
2923 header->lastPublicKey = htodl(p->getKeyStrings().size());
2924
2925 flatPackages.add(data);
2926 }
2927
2928 // And now write out the final chunks.
2929 const size_t dataStart = dest->getSize();
2930
2931 {
2932 // blah
2933 ResTable_header header;
2934 memset(&header, 0, sizeof(header));
2935 header.header.type = htods(RES_TABLE_TYPE);
2936 header.header.headerSize = htods(sizeof(header));
2937 header.packageCount = htodl(flatPackages.size());
2938 status_t err = dest->writeData(&header, sizeof(header));
2939 if (err != NO_ERROR) {
2940 fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
2941 return err;
2942 }
2943 }
2944
2945 ssize_t strStart = dest->getSize();
2946 err = valueStrings.writeStringBlock(dest);
2947 if (err != NO_ERROR) {
2948 return err;
2949 }
2950
2951 ssize_t amt = (dest->getSize()-strStart);
2952 strAmt += amt;
2953 #if PRINT_STRING_METRICS
2954 fprintf(stderr, "**** value strings: %d\n", amt);
2955 fprintf(stderr, "**** total strings: %d\n", strAmt);
2956 #endif
2957
2958 for (pi=0; pi<flatPackages.size(); pi++) {
2959 err = dest->writeData(flatPackages[pi]->getData(),
2960 flatPackages[pi]->getSize());
2961 if (err != NO_ERROR) {
2962 fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
2963 return err;
2964 }
2965 }
2966
2967 ResTable_header* header = (ResTable_header*)
2968 (((uint8_t*)dest->getData()) + dataStart);
2969 header->header.size = htodl(dest->getSize() - dataStart);
2970
2971 NOISY(aout << "Resource table:"
2972 << HexDump(dest->getData(), dest->getSize()) << endl);
2973
2974 #if PRINT_STRING_METRICS
2975 fprintf(stderr, "**** total resource table size: %d / %d%% strings\n",
2976 dest->getSize(), (strAmt*100)/dest->getSize());
2977 #endif
2978
2979 return NO_ERROR;
2980}
2981
2982void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
2983{
2984 fprintf(fp,
2985 "<!-- This file contains <public> resource definitions for all\n"
2986 " resources that were generated from the source data. -->\n"
2987 "\n"
2988 "<resources>\n");
2989
2990 writePublicDefinitions(package, fp, true);
2991 writePublicDefinitions(package, fp, false);
2992
2993 fprintf(fp,
2994 "\n"
2995 "</resources>\n");
2996}
2997
2998void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
2999{
3000 bool didHeader = false;
3001
3002 sp<Package> pkg = mPackages.valueFor(package);
3003 if (pkg != NULL) {
3004 const size_t NT = pkg->getOrderedTypes().size();
3005 for (size_t i=0; i<NT; i++) {
3006 sp<Type> t = pkg->getOrderedTypes().itemAt(i);
3007 if (t == NULL) {
3008 continue;
3009 }
3010
3011 bool didType = false;
3012
3013 const size_t NC = t->getOrderedConfigs().size();
3014 for (size_t j=0; j<NC; j++) {
3015 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
3016 if (c == NULL) {
3017 continue;
3018 }
3019
3020 if (c->getPublic() != pub) {
3021 continue;
3022 }
3023
3024 if (!didType) {
3025 fprintf(fp, "\n");
3026 didType = true;
3027 }
3028 if (!didHeader) {
3029 if (pub) {
3030 fprintf(fp," <!-- PUBLIC SECTION. These resources have been declared public.\n");
3031 fprintf(fp," Changes to these definitions will break binary compatibility. -->\n\n");
3032 } else {
3033 fprintf(fp," <!-- PRIVATE SECTION. These resources have not been declared public.\n");
3034 fprintf(fp," You can make them public my moving these lines into a file in res/values. -->\n\n");
3035 }
3036 didHeader = true;
3037 }
3038 if (!pub) {
3039 const size_t NE = c->getEntries().size();
3040 for (size_t k=0; k<NE; k++) {
3041 const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
3042 if (pos.file != "") {
3043 fprintf(fp," <!-- Declared at %s:%d -->\n",
3044 pos.file.string(), pos.line);
3045 }
3046 }
3047 }
3048 fprintf(fp, " <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
3049 String8(t->getName()).string(),
3050 String8(c->getName()).string(),
3051 getResId(pkg, t, c->getEntryIndex()));
3052 }
3053 }
3054 }
3055}
3056
3057ResourceTable::Item::Item(const SourcePos& _sourcePos,
3058 bool _isId,
3059 const String16& _value,
3060 const Vector<StringPool::entry_style_span>* _style,
3061 int32_t _format)
3062 : sourcePos(_sourcePos)
3063 , isId(_isId)
3064 , value(_value)
3065 , format(_format)
3066 , bagKeyId(0)
3067 , evaluating(false)
3068{
3069 if (_style) {
3070 style = *_style;
3071 }
3072}
3073
3074status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
3075{
3076 if (mType == TYPE_BAG) {
3077 return NO_ERROR;
3078 }
3079 if (mType == TYPE_UNKNOWN) {
3080 mType = TYPE_BAG;
3081 return NO_ERROR;
3082 }
3083 sourcePos.error("Resource entry %s is already defined as a single item.\n"
3084 "%s:%d: Originally defined here.\n",
3085 String8(mName).string(),
3086 mItem.sourcePos.file.string(), mItem.sourcePos.line);
3087 return UNKNOWN_ERROR;
3088}
3089
3090status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
3091 const String16& value,
3092 const Vector<StringPool::entry_style_span>* style,
3093 int32_t format,
3094 const bool overwrite)
3095{
3096 Item item(sourcePos, false, value, style);
3097
3098 if (mType == TYPE_BAG) {
3099 const Item& item(mBag.valueAt(0));
3100 sourcePos.error("Resource entry %s is already defined as a bag.\n"
3101 "%s:%d: Originally defined here.\n",
3102 String8(mName).string(),
3103 item.sourcePos.file.string(), item.sourcePos.line);
3104 return UNKNOWN_ERROR;
3105 }
3106 if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
3107 sourcePos.error("Resource entry %s is already defined.\n"
3108 "%s:%d: Originally defined here.\n",
3109 String8(mName).string(),
3110 mItem.sourcePos.file.string(), mItem.sourcePos.line);
3111 return UNKNOWN_ERROR;
3112 }
6c9f72e4 3113
a534180c
TAOSP
3114 mType = TYPE_ITEM;
3115 mItem = item;
3116 mItemFormat = format;
3117 return NO_ERROR;
3118}
3119
3120status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
3121 const String16& key, const String16& value,
3122 const Vector<StringPool::entry_style_span>* style,
3123 bool replace, bool isId, int32_t format)
3124{
3125 status_t err = makeItABag(sourcePos);
3126 if (err != NO_ERROR) {
3127 return err;
3128 }
3129
3130 Item item(sourcePos, isId, value, style, format);
3131
3132 // XXX NOTE: there is an error if you try to have a bag with two keys,
3133 // one an attr and one an id, with the same name. Not something we
3134 // currently ever have to worry about.
3135 ssize_t origKey = mBag.indexOfKey(key);
3136 if (origKey >= 0) {
3137 if (!replace) {
3138 const Item& item(mBag.valueAt(origKey));
3139 sourcePos.error("Resource entry %s already has bag item %s.\n"
3140 "%s:%d: Originally defined here.\n",
3141 String8(mName).string(), String8(key).string(),
3142 item.sourcePos.file.string(), item.sourcePos.line);
3143 return UNKNOWN_ERROR;
3144 }
3145 //printf("Replacing %s with %s\n",
3146 // String8(mBag.valueFor(key).value).string(), String8(value).string());
3147 mBag.replaceValueFor(key, item);
3148 }
3149
3150 mBag.add(key, item);
3151 return NO_ERROR;
3152}
3153
76486813
RG
3154status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
3155{
3156 status_t err = makeItABag(sourcePos);
3157 if (err != NO_ERROR) {
3158 return err;
3159 }
3160
3161 mBag.clear();
3162 return NO_ERROR;
3163}
3164
a534180c
TAOSP
3165status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
3166 const String16& package)
3167{
3168 const String16 attr16("attr");
3169 const String16 id16("id");
3170 const size_t N = mBag.size();
3171 for (size_t i=0; i<N; i++) {
3172 const String16& key = mBag.keyAt(i);
3173 const Item& it = mBag.valueAt(i);
3174 if (it.isId) {
3175 if (!table->hasBagOrEntry(key, &id16, &package)) {
3176 String16 value("false");
3177 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
3178 id16, key, value);
3179 if (err != NO_ERROR) {
3180 return err;
3181 }
3182 }
3183 } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
3184
3185#if 1
3186// fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
3187// String8(key).string());
3188// const Item& item(mBag.valueAt(i));
3189// fprintf(stderr, "Referenced from file %s line %d\n",
3190// item.sourcePos.file.string(), item.sourcePos.line);
3191// return UNKNOWN_ERROR;
3192#else
3193 char numberStr[16];
3194 sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
3195 status_t err = table->addBag(SourcePos("<generated>", 0), package,
3196 attr16, key, String16(""),
3197 String16("^type"),
3198 String16(numberStr), NULL, NULL);
3199 if (err != NO_ERROR) {
3200 return err;
3201 }
3202#endif
3203 }
3204 }
3205 return NO_ERROR;
3206}
3207
3208status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
3209 const String16& package)
3210{
3211 bool hasErrors = false;
3212
3213 if (mType == TYPE_BAG) {
3214 const char* errorMsg;
3215 const String16 style16("style");
3216 const String16 attr16("attr");
3217 const String16 id16("id");
3218 mParentId = 0;
3219 if (mParent.size() > 0) {
afc833f3 3220 mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
a534180c
TAOSP
3221 if (mParentId == 0) {
3222 mPos.error("Error retrieving parent for item: %s '%s'.\n",
3223 errorMsg, String8(mParent).string());
3224 hasErrors = true;
3225 }
3226 }
3227 const size_t N = mBag.size();
3228 for (size_t i=0; i<N; i++) {
3229 const String16& key = mBag.keyAt(i);
3230 Item& it = mBag.editValueAt(i);
3231 it.bagKeyId = table->getResId(key,
afc833f3 3232 it.isId ? &id16 : &attr16, NULL, &errorMsg);
a534180c
TAOSP
3233 //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
3234 if (it.bagKeyId == 0) {
3235 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
3236 String8(it.isId ? id16 : attr16).string(),
3237 String8(key).string());
3238 hasErrors = true;
3239 }
3240 }
3241 }
3242 return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
3243}
3244
4f41c7f7
DH
3245status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table,
3246 const String8* configTypeName, const ConfigDescription* config)
a534180c
TAOSP
3247{
3248 if (mType == TYPE_ITEM) {
3249 Item& it = mItem;
3250 AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
3251 if (!table->stringToValue(&it.parsedValue, strings,
3252 it.value, false, true, 0,
4f41c7f7
DH
3253 &it.style, NULL, &ac, mItemFormat,
3254 configTypeName, config)) {
a534180c
TAOSP
3255 return UNKNOWN_ERROR;
3256 }
3257 } else if (mType == TYPE_BAG) {
3258 const size_t N = mBag.size();
3259 for (size_t i=0; i<N; i++) {
3260 const String16& key = mBag.keyAt(i);
3261 Item& it = mBag.editValueAt(i);
3262 AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
3263 if (!table->stringToValue(&it.parsedValue, strings,
3264 it.value, false, true, it.bagKeyId,
4f41c7f7
DH
3265 &it.style, NULL, &ac, it.format,
3266 configTypeName, config)) {
a534180c
TAOSP
3267 return UNKNOWN_ERROR;
3268 }
3269 }
3270 } else {
3271 mPos.error("Error: entry %s is not a single item or a bag.\n",
3272 String8(mName).string());
3273 return UNKNOWN_ERROR;
3274 }
3275 return NO_ERROR;
3276}
3277
4f41c7f7
DH
3278status_t ResourceTable::Entry::remapStringValue(StringPool* strings)
3279{
3280 if (mType == TYPE_ITEM) {
3281 Item& it = mItem;
3282 if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
3283 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
3284 }
3285 } else if (mType == TYPE_BAG) {
3286 const size_t N = mBag.size();
3287 for (size_t i=0; i<N; i++) {
3288 Item& it = mBag.editValueAt(i);
3289 if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
3290 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
3291 }
3292 }
3293 } else {
3294 mPos.error("Error: entry %s is not a single item or a bag.\n",
3295 String8(mName).string());
3296 return UNKNOWN_ERROR;
3297 }
3298 return NO_ERROR;
3299}
3300
a534180c
TAOSP
3301ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
3302{
3303 size_t amt = 0;
3304 ResTable_entry header;
3305 memset(&header, 0, sizeof(header));
3306 header.size = htods(sizeof(header));
3307 const type ty = this != NULL ? mType : TYPE_ITEM;
3308 if (this != NULL) {
3309 if (ty == TYPE_BAG) {
3310 header.flags |= htods(header.FLAG_COMPLEX);
3311 }
3312 if (isPublic) {
3313 header.flags |= htods(header.FLAG_PUBLIC);
3314 }
3315 header.key.index = htodl(mNameIndex);
3316 }
3317 if (ty != TYPE_BAG) {
3318 status_t err = data->writeData(&header, sizeof(header));
3319 if (err != NO_ERROR) {
3320 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3321 return err;
3322 }
3323
3324 const Item& it = mItem;
3325 Res_value par;
3326 memset(&par, 0, sizeof(par));
3327 par.size = htods(it.parsedValue.size);
3328 par.dataType = it.parsedValue.dataType;
3329 par.res0 = it.parsedValue.res0;
3330 par.data = htodl(it.parsedValue.data);
3331 #if 0
3332 printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
3333 String8(mName).string(), it.parsedValue.dataType,
3334 it.parsedValue.data, par.res0);
3335 #endif
3336 err = data->writeData(&par, it.parsedValue.size);
3337 if (err != NO_ERROR) {
3338 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3339 return err;
3340 }
3341 amt += it.parsedValue.size;
3342 } else {
3343 size_t N = mBag.size();
3344 size_t i;
3345 // Create correct ordering of items.
3346 KeyedVector<uint32_t, const Item*> items;
3347 for (i=0; i<N; i++) {
3348 const Item& it = mBag.valueAt(i);
3349 items.add(it.bagKeyId, &it);
3350 }
3351 N = items.size();
3352
3353 ResTable_map_entry mapHeader;
3354 memcpy(&mapHeader, &header, sizeof(header));
3355 mapHeader.size = htods(sizeof(mapHeader));
3356 mapHeader.parent.ident = htodl(mParentId);
3357 mapHeader.count = htodl(N);
3358 status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
3359 if (err != NO_ERROR) {
3360 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3361 return err;
3362 }
3363
3364 for (i=0; i<N; i++) {
3365 const Item& it = *items.valueAt(i);
3366 ResTable_map map;
3367 map.name.ident = htodl(it.bagKeyId);
3368 map.value.size = htods(it.parsedValue.size);
3369 map.value.dataType = it.parsedValue.dataType;
3370 map.value.res0 = it.parsedValue.res0;
3371 map.value.data = htodl(it.parsedValue.data);
3372 err = data->writeData(&map, sizeof(map));
3373 if (err != NO_ERROR) {
3374 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3375 return err;
3376 }
3377 amt += sizeof(map);
3378 }
3379 }
3380 return amt;
3381}
3382
3383void ResourceTable::ConfigList::appendComment(const String16& comment,
3384 bool onlyIfEmpty)
3385{
3386 if (comment.size() <= 0) {
3387 return;
3388 }
3389 if (onlyIfEmpty && mComment.size() > 0) {
3390 return;
3391 }
3392 if (mComment.size() > 0) {
3393 mComment.append(String16("\n"));
3394 }
3395 mComment.append(comment);
3396}
3397
3398void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
3399{
3400 if (comment.size() <= 0) {
3401 return;
3402 }
3403 if (mTypeComment.size() > 0) {
3404 mTypeComment.append(String16("\n"));
3405 }
3406 mTypeComment.append(comment);
3407}
3408
3409status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
3410 const String16& name,
3411 const uint32_t ident)
3412{
3413 #if 0
3414 int32_t entryIdx = Res_GETENTRY(ident);
3415 if (entryIdx < 0) {
3416 sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
3417 String8(mName).string(), String8(name).string(), ident);
3418 return UNKNOWN_ERROR;
3419 }
3420 #endif
3421
3422 int32_t typeIdx = Res_GETTYPE(ident);
3423 if (typeIdx >= 0) {
3424 typeIdx++;
3425 if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
3426 sourcePos.error("Public resource %s/%s has conflicting type codes for its"
3427 " public identifiers (0x%x vs 0x%x).\n",
3428 String8(mName).string(), String8(name).string(),
3429 mPublicIndex, typeIdx);
3430 return UNKNOWN_ERROR;
3431 }
3432 mPublicIndex = typeIdx;
3433 }
3434
3435 if (mFirstPublicSourcePos == NULL) {
3436 mFirstPublicSourcePos = new SourcePos(sourcePos);
3437 }
3438
3439 if (mPublic.indexOfKey(name) < 0) {
3440 mPublic.add(name, Public(sourcePos, String16(), ident));
3441 } else {
3442 Public& p = mPublic.editValueFor(name);
3443 if (p.ident != ident) {
3444 sourcePos.error("Public resource %s/%s has conflicting public identifiers"
3445 " (0x%08x vs 0x%08x).\n"
3446 "%s:%d: Originally defined here.\n",
3447 String8(mName).string(), String8(name).string(), p.ident, ident,
3448 p.sourcePos.file.string(), p.sourcePos.line);
3449 return UNKNOWN_ERROR;
3450 }
3451 }
3452
3453 return NO_ERROR;
3454}
3455
a8c9cd56
DH
3456void ResourceTable::Type::canAddEntry(const String16& name)
3457{
3458 mCanAddEntries.add(name);
3459}
3460
a534180c
TAOSP
3461sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
3462 const SourcePos& sourcePos,
3463 const ResTable_config* config,
6c9f72e4 3464 bool doSetIndex,
636d3b70
XD
3465 bool overlay,
3466 bool autoAddOverlay)
a534180c
TAOSP
3467{
3468 int pos = -1;
3469 sp<ConfigList> c = mConfigs.valueFor(entry);
3470 if (c == NULL) {
636d3b70 3471 if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
a8c9cd56
DH
3472 sourcePos.error("Resource at %s appears in overlay but not"
3473 " in the base package; use <add-resource> to add.\n",
3474 String8(entry).string());
6c9f72e4
RG
3475 return NULL;
3476 }
a534180c
TAOSP
3477 c = new ConfigList(entry, sourcePos);
3478 mConfigs.add(entry, c);
3479 pos = (int)mOrderedConfigs.size();
3480 mOrderedConfigs.add(c);
3481 if (doSetIndex) {
3482 c->setEntryIndex(pos);
3483 }
3484 }
3485
3486 ConfigDescription cdesc;
3487 if (config) cdesc = *config;
3488
3489 sp<Entry> e = c->getEntries().valueFor(cdesc);
3490 if (e == NULL) {
3491 if (config != NULL) {
3492 NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
f1f3915b 3493 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
c3204d6e 3494 "sw%ddp w%ddp h%ddp dir:%d\n",
a534180c
TAOSP
3495 sourcePos.file.string(), sourcePos.line,
3496 config->mcc, config->mnc,
3497 config->language[0] ? config->language[0] : '-',
3498 config->language[1] ? config->language[1] : '-',
3499 config->country[0] ? config->country[0] : '-',
3500 config->country[1] ? config->country[1] : '-',
3501 config->orientation,
3502 config->touchscreen,
3503 config->density,
3504 config->keyboard,
3505 config->inputFlags,
3506 config->navigation,
3507 config->screenWidth,
4ee37df2 3508 config->screenHeight,
f1f3915b 3509 config->smallestScreenWidthDp,
4ee37df2 3510 config->screenWidthDp,
c3204d6e
FDM
3511 config->screenHeightDp,
3512 config->layoutDirection));
a534180c
TAOSP
3513 } else {
3514 NOISY(printf("New entry at %s:%d: NULL config\n",
3515 sourcePos.file.string(), sourcePos.line));
3516 }
3517 e = new Entry(entry, sourcePos);
3518 c->addEntry(cdesc, e);
3519 /*
3520 if (doSetIndex) {
3521 if (pos < 0) {
3522 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
3523 if (mOrderedConfigs[pos] == c) {
3524 break;
3525 }
3526 }
3527 if (pos >= (int)mOrderedConfigs.size()) {
3528 sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
3529 return NULL;
3530 }
3531 }
3532 e->setEntryIndex(pos);
3533 }
3534 */
3535 }
3536
3537 mUniqueConfigs.add(cdesc);
3538
3539 return e;
3540}
3541
3542status_t ResourceTable::Type::applyPublicEntryOrder()
3543{
3544 size_t N = mOrderedConfigs.size();
3545 Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
3546 bool hasError = false;
3547
3548 size_t i;
3549 for (i=0; i<N; i++) {
3550 mOrderedConfigs.replaceAt(NULL, i);
3551 }
3552
3553 const size_t NP = mPublic.size();
3554 //printf("Ordering %d configs from %d public defs\n", N, NP);
3555 size_t j;
3556 for (j=0; j<NP; j++) {
3557 const String16& name = mPublic.keyAt(j);
3558 const Public& p = mPublic.valueAt(j);
3559 int32_t idx = Res_GETENTRY(p.ident);
3560 //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
3561 // String8(mName).string(), String8(name).string(), p.ident, N);
3562 bool found = false;
3563 for (i=0; i<N; i++) {
3564 sp<ConfigList> e = origOrder.itemAt(i);
3565 //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
3566 if (e->getName() == name) {
3567 if (idx >= (int32_t)mOrderedConfigs.size()) {
3568 p.sourcePos.error("Public entry identifier 0x%x entry index "
3569 "is larger than available symbols (index %d, total symbols %d).\n",
3570 p.ident, idx, mOrderedConfigs.size());
3571 hasError = true;
3572 } else if (mOrderedConfigs.itemAt(idx) == NULL) {
3573 e->setPublic(true);
3574 e->setPublicSourcePos(p.sourcePos);
3575 mOrderedConfigs.replaceAt(e, idx);
3576 origOrder.removeAt(i);
3577 N--;
3578 found = true;
3579 break;
3580 } else {
3581 sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
3582
3583 p.sourcePos.error("Multiple entry names declared for public entry"
3584 " identifier 0x%x in type %s (%s vs %s).\n"
3585 "%s:%d: Originally defined here.",
3586 idx+1, String8(mName).string(),
3587 String8(oe->getName()).string(),
3588 String8(name).string(),
3589 oe->getPublicSourcePos().file.string(),
3590 oe->getPublicSourcePos().line);
3591 hasError = true;
3592 }
3593 }
3594 }
3595
3596 if (!found) {
3597 p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
3598 String8(mName).string(), String8(name).string());
3599 hasError = true;
3600 }
3601 }
3602
3603 //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
3604
3605 if (N != origOrder.size()) {
3606 printf("Internal error: remaining private symbol count mismatch\n");
3607 N = origOrder.size();
3608 }
3609
3610 j = 0;
3611 for (i=0; i<N; i++) {
3612 sp<ConfigList> e = origOrder.itemAt(i);
3613 // There will always be enough room for the remaining entries.
3614 while (mOrderedConfigs.itemAt(j) != NULL) {
3615 j++;
3616 }
3617 mOrderedConfigs.replaceAt(e, j);
3618 j++;
3619 }
3620
3621 return hasError ? UNKNOWN_ERROR : NO_ERROR;
3622}
3623
3624ResourceTable::Package::Package(const String16& name, ssize_t includedId)
3625 : mName(name), mIncludedId(includedId),
3626 mTypeStringsMapping(0xffffffff),
3627 mKeyStringsMapping(0xffffffff)
3628{
3629}
3630
3631sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
3632 const SourcePos& sourcePos,
3633 bool doSetIndex)
3634{
3635 sp<Type> t = mTypes.valueFor(type);
3636 if (t == NULL) {
3637 t = new Type(type, sourcePos);
3638 mTypes.add(type, t);
3639 mOrderedTypes.add(t);
3640 if (doSetIndex) {
3641 // For some reason the type's index is set to one plus the index
3642 // in the mOrderedTypes list, rather than just the index.
3643 t->setIndex(mOrderedTypes.size());
3644 }
3645 }
3646 return t;
3647}
3648
3649status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
3650{
3651 mTypeStringsData = data;
3652 status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
3653 if (err != NO_ERROR) {
3654 fprintf(stderr, "ERROR: Type string data is corrupt!\n");
3655 }
3656 return err;
3657}
3658
3659status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
3660{
3661 mKeyStringsData = data;
3662 status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
3663 if (err != NO_ERROR) {
3664 fprintf(stderr, "ERROR: Key string data is corrupt!\n");
3665 }
3666 return err;
3667}
3668
3669status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
3670 ResStringPool* strings,
3671 DefaultKeyedVector<String16, uint32_t>* mappings)
3672{
3673 if (data->getData() == NULL) {
3674 return UNKNOWN_ERROR;
3675 }
3676
3677 NOISY(aout << "Setting restable string pool: "
3678 << HexDump(data->getData(), data->getSize()) << endl);
3679
3680 status_t err = strings->setTo(data->getData(), data->getSize());
3681 if (err == NO_ERROR) {
3682 const size_t N = strings->size();
3683 for (size_t i=0; i<N; i++) {
3684 size_t len;
3685 mappings->add(String16(strings->stringAt(i, &len)), i);
3686 }
3687 }
3688 return err;
3689}
3690
3691status_t ResourceTable::Package::applyPublicTypeOrder()
3692{
3693 size_t N = mOrderedTypes.size();
3694 Vector<sp<Type> > origOrder(mOrderedTypes);
3695
3696 size_t i;
3697 for (i=0; i<N; i++) {
3698 mOrderedTypes.replaceAt(NULL, i);
3699 }
3700
3701 for (i=0; i<N; i++) {
3702 sp<Type> t = origOrder.itemAt(i);
3703 int32_t idx = t->getPublicIndex();
3704 if (idx > 0) {
3705 idx--;
3706 while (idx >= (int32_t)mOrderedTypes.size()) {
3707 mOrderedTypes.add();
3708 }
3709 if (mOrderedTypes.itemAt(idx) != NULL) {
3710 sp<Type> ot = mOrderedTypes.itemAt(idx);
3711 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
3712 " identifier 0x%x (%s vs %s).\n"
3713 "%s:%d: Originally defined here.",
3714 idx, String8(ot->getName()).string(),
3715 String8(t->getName()).string(),
3716 ot->getFirstPublicSourcePos().file.string(),
3717 ot->getFirstPublicSourcePos().line);
3718 return UNKNOWN_ERROR;
3719 }
3720 mOrderedTypes.replaceAt(t, idx);
3721 origOrder.removeAt(i);
3722 i--;
3723 N--;
3724 }
3725 }
3726
3727 size_t j=0;
3728 for (i=0; i<N; i++) {
3729 sp<Type> t = origOrder.itemAt(i);
3730 // There will always be enough room for the remaining types.
3731 while (mOrderedTypes.itemAt(j) != NULL) {
3732 j++;
3733 }
3734 mOrderedTypes.replaceAt(t, j);
3735 }
3736
3737 return NO_ERROR;
3738}
3739
3740sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
3741{
3742 sp<Package> p = mPackages.valueFor(package);
3743 if (p == NULL) {
08a9acaa 3744 if (mIsAppPackage) {
a534180c
TAOSP
3745 if (mHaveAppPackage) {
3746 fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
3747 "Use -x to create extended resources.\n");
3748 return NULL;
3749 }
3750 mHaveAppPackage = true;
3751 p = new Package(package, 127);
3752 } else {
3753 p = new Package(package, mNextPackageId);
3754 }
3755 //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
3756 // String8(package).string(), p->getAssignedId());
3757 mPackages.add(package, p);
3758 mOrderedPackages.add(p);
3759 mNextPackageId++;
3760 }
3761 return p;
3762}
3763
3764sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
3765 const String16& type,
3766 const SourcePos& sourcePos,
3767 bool doSetIndex)
3768{
3769 sp<Package> p = getPackage(package);
3770 if (p == NULL) {
3771 return NULL;
3772 }
3773 return p->getType(type, sourcePos, doSetIndex);
3774}
3775
3776sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
3777 const String16& type,
3778 const String16& name,
3779 const SourcePos& sourcePos,
6c9f72e4 3780 bool overlay,
a534180c
TAOSP
3781 const ResTable_config* config,
3782 bool doSetIndex)
3783{
3784 sp<Type> t = getType(package, type, sourcePos, doSetIndex);
3785 if (t == NULL) {
3786 return NULL;
3787 }
636d3b70 3788 return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
a534180c
TAOSP
3789}
3790
3791sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
3792 const ResTable_config* config) const
3793{
3794 int pid = Res_GETPACKAGE(resID)+1;
3795 const size_t N = mOrderedPackages.size();
3796 size_t i;
3797 sp<Package> p;
3798 for (i=0; i<N; i++) {
3799 sp<Package> check = mOrderedPackages[i];
3800 if (check->getAssignedId() == pid) {
3801 p = check;
3802 break;
3803 }
3804
3805 }
3806 if (p == NULL) {
406a85e3 3807 fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
a534180c
TAOSP
3808 return NULL;
3809 }
3810
3811 int tid = Res_GETTYPE(resID);
3812 if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
406a85e3 3813 fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
a534180c
TAOSP
3814 return NULL;
3815 }
3816 sp<Type> t = p->getOrderedTypes()[tid];
3817
3818 int eid = Res_GETENTRY(resID);
3819 if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
406a85e3 3820 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
a534180c
TAOSP
3821 return NULL;
3822 }
3823
3824 sp<ConfigList> c = t->getOrderedConfigs()[eid];
3825 if (c == NULL) {
406a85e3 3826 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
a534180c
TAOSP
3827 return NULL;
3828 }
3829
3830 ConfigDescription cdesc;
3831 if (config) cdesc = *config;
3832 sp<Entry> e = c->getEntries().valueFor(cdesc);
3833 if (c == NULL) {
406a85e3 3834 fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
a534180c
TAOSP
3835 return NULL;
3836 }
3837
3838 return e;
3839}
3840
3841const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
3842{
3843 sp<const Entry> e = getEntry(resID);
3844 if (e == NULL) {
3845 return NULL;
3846 }
3847
3848 const size_t N = e->getBag().size();
3849 for (size_t i=0; i<N; i++) {
3850 const Item& it = e->getBag().valueAt(i);
3851 if (it.bagKeyId == 0) {
406a85e3 3852 fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
a534180c
TAOSP
3853 String8(e->getName()).string(),
3854 String8(e->getBag().keyAt(i)).string());
3855 }
3856 if (it.bagKeyId == attrID) {
3857 return &it;
3858 }
3859 }
3860
3861 return NULL;
3862}
3863
3864bool ResourceTable::getItemValue(
3865 uint32_t resID, uint32_t attrID, Res_value* outValue)
3866{
3867 const Item* item = getItem(resID, attrID);
3868
3869 bool res = false;
3870 if (item != NULL) {
3871 if (item->evaluating) {
3872 sp<const Entry> e = getEntry(resID);
3873 const size_t N = e->getBag().size();
3874 size_t i;
3875 for (i=0; i<N; i++) {
3876 if (&e->getBag().valueAt(i) == item) {
3877 break;
3878 }
3879 }
406a85e3 3880 fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
a534180c
TAOSP
3881 String8(e->getName()).string(),
3882 String8(e->getBag().keyAt(i)).string());
3883 return false;
3884 }
3885 item->evaluating = true;
3886 res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
3887 NOISY(
3888 if (res) {
3889 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
3890 resID, attrID, String8(getEntry(resID)->getName()).string(),
3891 outValue->dataType, outValue->data);
3892 } else {
3893 printf("getItemValue of #%08x[#%08x]: failed\n",
3894 resID, attrID);
3895 }
3896 );
3897 item->evaluating = false;
3898 }
3899 return res;
3900}