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