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