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