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