blob: 770c7d816cdfc205d66c0f1eb696eb53ece4bdc5 [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
1519
1520 sp<Entry> e = getEntry(package, type, name, sourcePos, params, doSetIndex);
1521 if (e == NULL) {
1522 return UNKNOWN_ERROR;
1523 }
1524 status_t err = e->setItem(sourcePos, value, style, format, overwrite);
1525 if (err == NO_ERROR) {
1526 mNumLocal++;
1527 }
1528 return err;
1529}
1530
1531status_t ResourceTable::startBag(const SourcePos& sourcePos,
1532 const String16& package,
1533 const String16& type,
1534 const String16& name,
1535 const String16& bagParent,
1536 const ResTable_config* params,
Robert Greenwalt1aa81702009-06-05 15:59:15 -07001537 bool overlay,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001538 bool replace, bool isId)
1539{
Robert Greenwalt4b4f4a92009-04-02 16:55:50 -07001540 status_t result = NO_ERROR;
1541
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001542 // Check for adding entries in other packages... for now we do
1543 // nothing. We need to do the right thing here to support skinning.
1544 uint32_t rid = mAssets->getIncludedResources()
1545 .identifierForName(name.string(), name.size(),
1546 type.string(), type.size(),
1547 package.string(), package.size());
1548 if (rid != 0) {
1549 return NO_ERROR;
1550 }
1551
1552#if 0
1553 if (name == String16("left")) {
1554 printf("Adding bag left: file=%s, line=%d, type=%s\n",
1555 sourcePos.file.striing(), sourcePos.line, String8(type).string());
1556 }
1557#endif
Robert Greenwalt1aa81702009-06-05 15:59:15 -07001558 if (overlay && !hasBagOrEntry(package, type, name)) {
1559 sourcePos.error("Can't add new bags in an overlay. See '%s'\n",
1560 String8(name).string());
1561 return UNKNOWN_ERROR;
1562 }
1563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 sp<Entry> e = getEntry(package, type, name, sourcePos, params);
1565 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
1618
1619 sp<Entry> e = getEntry(package, type, name, sourcePos, params);
1620 if (e == NULL) {
1621 return UNKNOWN_ERROR;
1622 }
1623
1624 // If a parent is explicitly specified, set it.
1625 if (bagParent.size() > 0) {
1626 String16 curPar = e->getParent();
1627 if (curPar.size() > 0 && curPar != bagParent) {
1628 sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
1629 String8(e->getParent()).string(),
1630 String8(bagParent).string());
1631 return UNKNOWN_ERROR;
1632 }
1633 e->setParent(bagParent);
1634 }
1635
1636 const bool first = e->getBag().indexOfKey(bagKey) < 0;
1637 status_t err = e->addToBag(sourcePos, bagKey, value, style, replace, isId, format);
1638 if (err == NO_ERROR && first) {
1639 mNumLocal++;
1640 }
1641 return err;
1642}
1643
1644bool ResourceTable::hasBagOrEntry(const String16& package,
1645 const String16& type,
1646 const String16& name) const
1647{
1648 // First look for this in the included resources...
1649 uint32_t rid = mAssets->getIncludedResources()
1650 .identifierForName(name.string(), name.size(),
1651 type.string(), type.size(),
1652 package.string(), package.size());
1653 if (rid != 0) {
1654 return true;
1655 }
1656
1657 sp<Package> p = mPackages.valueFor(package);
1658 if (p != NULL) {
1659 sp<Type> t = p->getTypes().valueFor(type);
1660 if (t != NULL) {
1661 sp<ConfigList> c = t->getConfigs().valueFor(name);
1662 if (c != NULL) return true;
1663 }
1664 }
1665
1666 return false;
1667}
1668
1669bool ResourceTable::hasBagOrEntry(const String16& ref,
1670 const String16* defType,
1671 const String16* defPackage)
1672{
1673 String16 package, type, name;
1674 if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
1675 defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
1676 return false;
1677 }
1678 return hasBagOrEntry(package, type, name);
1679}
1680
1681bool ResourceTable::appendComment(const String16& package,
1682 const String16& type,
1683 const String16& name,
1684 const String16& comment,
1685 bool onlyIfEmpty)
1686{
1687 if (comment.size() <= 0) {
1688 return true;
1689 }
1690
1691 sp<Package> p = mPackages.valueFor(package);
1692 if (p != NULL) {
1693 sp<Type> t = p->getTypes().valueFor(type);
1694 if (t != NULL) {
1695 sp<ConfigList> c = t->getConfigs().valueFor(name);
1696 if (c != NULL) {
1697 c->appendComment(comment, onlyIfEmpty);
1698 return true;
1699 }
1700 }
1701 }
1702 return false;
1703}
1704
1705bool ResourceTable::appendTypeComment(const String16& package,
1706 const String16& type,
1707 const String16& name,
1708 const String16& comment)
1709{
1710 if (comment.size() <= 0) {
1711 return true;
1712 }
1713
1714 sp<Package> p = mPackages.valueFor(package);
1715 if (p != NULL) {
1716 sp<Type> t = p->getTypes().valueFor(type);
1717 if (t != NULL) {
1718 sp<ConfigList> c = t->getConfigs().valueFor(name);
1719 if (c != NULL) {
1720 c->appendTypeComment(comment);
1721 return true;
1722 }
1723 }
1724 }
1725 return false;
1726}
1727
1728size_t ResourceTable::size() const {
1729 return mPackages.size();
1730}
1731
1732size_t ResourceTable::numLocalResources() const {
1733 return mNumLocal;
1734}
1735
1736bool ResourceTable::hasResources() const {
1737 return mNumLocal > 0;
1738}
1739
1740sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
1741{
1742 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
1743 status_t err = flatten(bundle, data);
1744 return err == NO_ERROR ? data : NULL;
1745}
1746
1747inline uint32_t ResourceTable::getResId(const sp<Package>& p,
1748 const sp<Type>& t,
1749 uint32_t nameId)
1750{
1751 return makeResId(p->getAssignedId(), t->getIndex(), nameId);
1752}
1753
1754uint32_t ResourceTable::getResId(const String16& package,
1755 const String16& type,
1756 const String16& name,
1757 bool onlyPublic) const
1758{
1759 sp<Package> p = mPackages.valueFor(package);
1760 if (p == NULL) return 0;
1761
1762 // First look for this in the included resources...
1763 uint32_t specFlags = 0;
1764 uint32_t rid = mAssets->getIncludedResources()
1765 .identifierForName(name.string(), name.size(),
1766 type.string(), type.size(),
1767 package.string(), package.size(),
1768 &specFlags);
1769 if (rid != 0) {
1770 if (onlyPublic) {
1771 if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
1772 return 0;
1773 }
1774 }
1775
1776 if (Res_INTERNALID(rid)) {
1777 return rid;
1778 }
1779 return Res_MAKEID(p->getAssignedId()-1,
1780 Res_GETTYPE(rid),
1781 Res_GETENTRY(rid));
1782 }
1783
1784 sp<Type> t = p->getTypes().valueFor(type);
1785 if (t == NULL) return 0;
1786 sp<ConfigList> c = t->getConfigs().valueFor(name);
1787 if (c == NULL) return 0;
1788 int32_t ei = c->getEntryIndex();
1789 if (ei < 0) return 0;
1790 return getResId(p, t, ei);
1791}
1792
1793uint32_t ResourceTable::getResId(const String16& ref,
1794 const String16* defType,
1795 const String16* defPackage,
1796 const char** outErrorMsg,
1797 bool onlyPublic) const
1798{
1799 String16 package, type, name;
1800 if (!ResTable::expandResourceRef(
1801 ref.string(), ref.size(), &package, &type, &name,
1802 defType, defPackage ? defPackage:&mAssetsPackage,
1803 outErrorMsg)) {
1804 NOISY(printf("Expanding resource: ref=%s\n",
1805 String8(ref).string()));
1806 NOISY(printf("Expanding resource: defType=%s\n",
1807 defType ? String8(*defType).string() : "NULL"));
1808 NOISY(printf("Expanding resource: defPackage=%s\n",
1809 defPackage ? String8(*defPackage).string() : "NULL"));
1810 NOISY(printf("Expanding resource: ref=%s\n", String8(ref).string()));
1811 NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
1812 String8(package).string(), String8(type).string(),
1813 String8(name).string()));
1814 return 0;
1815 }
1816 uint32_t res = getResId(package, type, name, onlyPublic);
1817 NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
1818 String8(package).string(), String8(type).string(),
1819 String8(name).string(), res));
1820 if (res == 0) {
1821 if (outErrorMsg)
1822 *outErrorMsg = "No resource found that matches the given name";
1823 }
1824 return res;
1825}
1826
1827bool ResourceTable::isValidResourceName(const String16& s)
1828{
1829 const char16_t* p = s.string();
1830 bool first = true;
1831 while (*p) {
1832 if ((*p >= 'a' && *p <= 'z')
1833 || (*p >= 'A' && *p <= 'Z')
1834 || *p == '_'
1835 || (!first && *p >= '0' && *p <= '9')) {
1836 first = false;
1837 p++;
1838 continue;
1839 }
1840 return false;
1841 }
1842 return true;
1843}
1844
1845bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
1846 const String16& str,
1847 bool preserveSpaces, bool coerceType,
1848 uint32_t attrID,
1849 const Vector<StringPool::entry_style_span>* style,
1850 String16* outStr, void* accessorCookie,
1851 uint32_t attrType)
1852{
1853 String16 finalStr;
1854
1855 bool res = true;
1856 if (style == NULL || style->size() == 0) {
1857 // Text is not styled so it can be any type... let's figure it out.
1858 res = mAssets->getIncludedResources()
1859 .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
1860 coerceType, attrID, NULL, &mAssetsPackage, this,
1861 accessorCookie, attrType);
1862 } else {
1863 // Styled text can only be a string, and while collecting the style
1864 // information we have already processed that string!
1865 outValue->size = sizeof(Res_value);
1866 outValue->res0 = 0;
1867 outValue->dataType = outValue->TYPE_STRING;
1868 outValue->data = 0;
1869 finalStr = str;
1870 }
1871
1872 if (!res) {
1873 return false;
1874 }
1875
1876 if (outValue->dataType == outValue->TYPE_STRING) {
1877 // Should do better merging styles.
1878 if (pool) {
1879 if (style != NULL && style->size() > 0) {
1880 outValue->data = pool->add(finalStr, *style);
1881 } else {
1882 outValue->data = pool->add(finalStr, true);
1883 }
1884 } else {
1885 // Caller will fill this in later.
1886 outValue->data = 0;
1887 }
1888
1889 if (outStr) {
1890 *outStr = finalStr;
1891 }
1892
1893 }
1894
1895 return true;
1896}
1897
1898uint32_t ResourceTable::getCustomResource(
1899 const String16& package, const String16& type, const String16& name) const
1900{
1901 //printf("getCustomResource: %s %s %s\n", String8(package).string(),
1902 // String8(type).string(), String8(name).string());
1903 sp<Package> p = mPackages.valueFor(package);
1904 if (p == NULL) return 0;
1905 sp<Type> t = p->getTypes().valueFor(type);
1906 if (t == NULL) return 0;
1907 sp<ConfigList> c = t->getConfigs().valueFor(name);
1908 if (c == NULL) return 0;
1909 int32_t ei = c->getEntryIndex();
1910 if (ei < 0) return 0;
1911 return getResId(p, t, ei);
1912}
1913
1914uint32_t ResourceTable::getCustomResourceWithCreation(
1915 const String16& package, const String16& type, const String16& name,
1916 const bool createIfNotFound)
1917{
1918 uint32_t resId = getCustomResource(package, type, name);
1919 if (resId != 0 || !createIfNotFound) {
1920 return resId;
1921 }
1922 String16 value("false");
1923
1924 status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
1925 if (status == NO_ERROR) {
1926 resId = getResId(package, type, name);
1927 return resId;
1928 }
1929 return 0;
1930}
1931
1932uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
1933{
1934 return origPackage;
1935}
1936
1937bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
1938{
1939 //printf("getAttributeType #%08x\n", attrID);
1940 Res_value value;
1941 if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
1942 //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
1943 // String8(getEntry(attrID)->getName()).string(), value.data);
1944 *outType = value.data;
1945 return true;
1946 }
1947 return false;
1948}
1949
1950bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
1951{
1952 //printf("getAttributeMin #%08x\n", attrID);
1953 Res_value value;
1954 if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
1955 *outMin = value.data;
1956 return true;
1957 }
1958 return false;
1959}
1960
1961bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
1962{
1963 //printf("getAttributeMax #%08x\n", attrID);
1964 Res_value value;
1965 if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
1966 *outMax = value.data;
1967 return true;
1968 }
1969 return false;
1970}
1971
1972uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
1973{
1974 //printf("getAttributeL10N #%08x\n", attrID);
1975 Res_value value;
1976 if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
1977 return value.data;
1978 }
1979 return ResTable_map::L10N_NOT_REQUIRED;
1980}
1981
1982bool ResourceTable::getLocalizationSetting()
1983{
1984 return mBundle->getRequireLocalization();
1985}
1986
1987void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
1988{
1989 if (accessorCookie != NULL && fmt != NULL) {
1990 AccessorCookie* ac = (AccessorCookie*)accessorCookie;
1991 int retval=0;
1992 char buf[1024];
1993 va_list ap;
1994 va_start(ap, fmt);
1995 retval = vsnprintf(buf, sizeof(buf), fmt, ap);
1996 va_end(ap);
1997 ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
1998 buf, ac->attr.string(), ac->value.string());
1999 }
2000}
2001
2002bool ResourceTable::getAttributeKeys(
2003 uint32_t attrID, Vector<String16>* outKeys)
2004{
2005 sp<const Entry> e = getEntry(attrID);
2006 if (e != NULL) {
2007 const size_t N = e->getBag().size();
2008 for (size_t i=0; i<N; i++) {
2009 const String16& key = e->getBag().keyAt(i);
2010 if (key.size() > 0 && key.string()[0] != '^') {
2011 outKeys->add(key);
2012 }
2013 }
2014 return true;
2015 }
2016 return false;
2017}
2018
2019bool ResourceTable::getAttributeEnum(
2020 uint32_t attrID, const char16_t* name, size_t nameLen,
2021 Res_value* outValue)
2022{
2023 //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
2024 String16 nameStr(name, nameLen);
2025 sp<const Entry> e = getEntry(attrID);
2026 if (e != NULL) {
2027 const size_t N = e->getBag().size();
2028 for (size_t i=0; i<N; i++) {
2029 //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
2030 // String8(e->getBag().keyAt(i)).string());
2031 if (e->getBag().keyAt(i) == nameStr) {
2032 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
2033 }
2034 }
2035 }
2036 return false;
2037}
2038
2039bool ResourceTable::getAttributeFlags(
2040 uint32_t attrID, const char16_t* name, size_t nameLen,
2041 Res_value* outValue)
2042{
2043 outValue->dataType = Res_value::TYPE_INT_HEX;
2044 outValue->data = 0;
2045
2046 //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
2047 String16 nameStr(name, nameLen);
2048 sp<const Entry> e = getEntry(attrID);
2049 if (e != NULL) {
2050 const size_t N = e->getBag().size();
2051
2052 const char16_t* end = name + nameLen;
2053 const char16_t* pos = name;
2054 bool failed = false;
2055 while (pos < end && !failed) {
2056 const char16_t* start = pos;
2057 end++;
2058 while (pos < end && *pos != '|') {
2059 pos++;
2060 }
2061
2062 String16 nameStr(start, pos-start);
2063 size_t i;
2064 for (i=0; i<N; i++) {
2065 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
2066 // String8(e->getBag().keyAt(i)).string());
2067 if (e->getBag().keyAt(i) == nameStr) {
2068 Res_value val;
2069 bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
2070 if (!got) {
2071 return false;
2072 }
2073 //printf("Got value: 0x%08x\n", val.data);
2074 outValue->data |= val.data;
2075 break;
2076 }
2077 }
2078
2079 if (i >= N) {
2080 // Didn't find this flag identifier.
2081 return false;
2082 }
2083 if (pos < end) {
2084 pos++;
2085 }
2086 }
2087
2088 return true;
2089 }
2090 return false;
2091}
2092
2093status_t ResourceTable::assignResourceIds()
2094{
2095 const size_t N = mOrderedPackages.size();
2096 size_t pi;
2097 status_t firstError = NO_ERROR;
2098
2099 // First generate all bag attributes and assign indices.
2100 for (pi=0; pi<N; pi++) {
2101 sp<Package> p = mOrderedPackages.itemAt(pi);
2102 if (p == NULL || p->getTypes().size() == 0) {
2103 // Empty, skip!
2104 continue;
2105 }
2106
2107 status_t err = p->applyPublicTypeOrder();
2108 if (err != NO_ERROR && firstError == NO_ERROR) {
2109 firstError = err;
2110 }
2111
2112 // Generate attributes...
2113 const size_t N = p->getOrderedTypes().size();
2114 size_t ti;
2115 for (ti=0; ti<N; ti++) {
2116 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2117 if (t == NULL) {
2118 continue;
2119 }
2120 const size_t N = t->getOrderedConfigs().size();
2121 for (size_t ci=0; ci<N; ci++) {
2122 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2123 if (c == NULL) {
2124 continue;
2125 }
2126 const size_t N = c->getEntries().size();
2127 for (size_t ei=0; ei<N; ei++) {
2128 sp<Entry> e = c->getEntries().valueAt(ei);
2129 if (e == NULL) {
2130 continue;
2131 }
2132 status_t err = e->generateAttributes(this, p->getName());
2133 if (err != NO_ERROR && firstError == NO_ERROR) {
2134 firstError = err;
2135 }
2136 }
2137 }
2138 }
2139
2140 const SourcePos unknown(String8("????"), 0);
2141 sp<Type> attr = p->getType(String16("attr"), unknown);
2142
2143 // Assign indices...
2144 for (ti=0; ti<N; ti++) {
2145 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2146 if (t == NULL) {
2147 continue;
2148 }
2149 err = t->applyPublicEntryOrder();
2150 if (err != NO_ERROR && firstError == NO_ERROR) {
2151 firstError = err;
2152 }
2153
2154 const size_t N = t->getOrderedConfigs().size();
2155 t->setIndex(ti+1);
2156
2157 LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
2158 "First type is not attr!");
2159
2160 for (size_t ei=0; ei<N; ei++) {
2161 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
2162 if (c == NULL) {
2163 continue;
2164 }
2165 c->setEntryIndex(ei);
2166 }
2167 }
2168
2169 // Assign resource IDs to keys in bags...
2170 for (ti=0; ti<N; ti++) {
2171 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2172 if (t == NULL) {
2173 continue;
2174 }
2175 const size_t N = t->getOrderedConfigs().size();
2176 for (size_t ci=0; ci<N; ci++) {
2177 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2178 //printf("Ordered config #%d: %p\n", ci, c.get());
2179 const size_t N = c->getEntries().size();
2180 for (size_t ei=0; ei<N; ei++) {
2181 sp<Entry> e = c->getEntries().valueAt(ei);
2182 if (e == NULL) {
2183 continue;
2184 }
2185 status_t err = e->assignResourceIds(this, p->getName());
2186 if (err != NO_ERROR && firstError == NO_ERROR) {
2187 firstError = err;
2188 }
2189 }
2190 }
2191 }
2192 }
2193 return firstError;
2194}
2195
2196status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
2197 const size_t N = mOrderedPackages.size();
2198 size_t pi;
2199
2200 for (pi=0; pi<N; pi++) {
2201 sp<Package> p = mOrderedPackages.itemAt(pi);
2202 if (p->getTypes().size() == 0) {
2203 // Empty, skip!
2204 continue;
2205 }
2206
2207 const size_t N = p->getOrderedTypes().size();
2208 size_t ti;
2209
2210 for (ti=0; ti<N; ti++) {
2211 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2212 if (t == NULL) {
2213 continue;
2214 }
2215 const size_t N = t->getOrderedConfigs().size();
2216 sp<AaptSymbols> typeSymbols;
2217 typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
2218 for (size_t ci=0; ci<N; ci++) {
2219 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2220 if (c == NULL) {
2221 continue;
2222 }
2223 uint32_t rid = getResId(p, t, ci);
2224 if (rid == 0) {
2225 return UNKNOWN_ERROR;
2226 }
2227 if (Res_GETPACKAGE(rid) == (size_t)(p->getAssignedId()-1)) {
2228 typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
2229
2230 String16 comment(c->getComment());
2231 typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
2232 //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(),
2233 // String8(comment).string());
2234 comment = c->getTypeComment();
2235 typeSymbols->appendTypeComment(String8(c->getName()), comment);
2236 } else {
2237#if 0
2238 printf("**** NO MATCH: 0x%08x vs 0x%08x\n",
2239 Res_GETPACKAGE(rid), p->getAssignedId());
2240#endif
2241 }
2242 }
2243 }
2244 }
2245 return NO_ERROR;
2246}
2247
2248
2249void
2250ResourceTable::addLocalization(const String16& name, const String8& locale)
2251{
2252 mLocalizations[name].insert(locale);
2253}
2254
2255
2256/*!
2257 * Flag various sorts of localization problems. '+' indicates checks already implemented;
2258 * '-' indicates checks that will be implemented in the future.
2259 *
2260 * + A localized string for which no default-locale version exists => warning
2261 * + A string for which no version in an explicitly-requested locale exists => warning
2262 * + A localized translation of an translateable="false" string => warning
2263 * - A localized string not provided in every locale used by the table
2264 */
2265status_t
2266ResourceTable::validateLocalizations(void)
2267{
2268 status_t err = NO_ERROR;
2269 const String8 defaultLocale;
2270
2271 // For all strings...
2272 for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
2273 nameIter != mLocalizations.end();
2274 nameIter++) {
2275 const set<String8>& configSet = nameIter->second; // naming convenience
2276
2277 // Look for strings with no default localization
2278 if (configSet.count(defaultLocale) == 0) {
2279 fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
2280 String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
2281 for (set<String8>::iterator locales = configSet.begin();
2282 locales != configSet.end();
2283 locales++) {
2284 fprintf(stdout, " %s", (*locales).string());
2285 }
2286 fprintf(stdout, "\n");
2287 // !!! TODO: throw an error here in some circumstances
2288 }
2289
2290 // Check that all requested localizations are present for this string
2291 if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
2292 const char* allConfigs = mBundle->getConfigurations();
2293 const char* start = allConfigs;
2294 const char* comma;
2295
2296 do {
2297 String8 config;
2298 comma = strchr(start, ',');
2299 if (comma != NULL) {
2300 config.setTo(start, comma - start);
2301 start = comma + 1;
2302 } else {
2303 config.setTo(start);
2304 }
2305
2306 // don't bother with the pseudolocale "zz_ZZ"
2307 if (config != "zz_ZZ") {
2308 if (configSet.find(config) == configSet.end()) {
2309 // okay, no specific localization found. it's possible that we are
2310 // requiring a specific regional localization [e.g. de_DE] but there is an
2311 // available string in the generic language localization [e.g. de];
2312 // consider that string to have fulfilled the localization requirement.
2313 String8 region(config.string(), 2);
2314 if (configSet.find(region) == configSet.end()) {
2315 if (configSet.count(defaultLocale) == 0) {
2316 fprintf(stdout, "aapt: error: "
2317 "*** string '%s' has no default or required localization "
2318 "for '%s' in %s\n",
2319 String8(nameIter->first).string(),
2320 config.string(),
2321 mBundle->getResourceSourceDirs()[0]);
2322 err = UNKNOWN_ERROR;
2323 }
2324 }
2325 }
2326 }
2327 } while (comma != NULL);
2328 }
2329 }
2330
2331 return err;
2332}
2333
2334
2335status_t
2336ResourceFilter::parse(const char* arg)
2337{
2338 if (arg == NULL) {
2339 return 0;
2340 }
2341
2342 const char* p = arg;
2343 const char* q;
2344
2345 while (true) {
2346 q = strchr(p, ',');
2347 if (q == NULL) {
2348 q = p + strlen(p);
2349 }
2350
2351 String8 part(p, q-p);
2352
2353 if (part == "zz_ZZ") {
2354 mContainsPseudo = true;
2355 }
2356 int axis;
2357 uint32_t value;
2358 if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
2359 fprintf(stderr, "Invalid configuration: %s\n", arg);
2360 fprintf(stderr, " ");
2361 for (int i=0; i<p-arg; i++) {
2362 fprintf(stderr, " ");
2363 }
2364 for (int i=0; i<q-p; i++) {
2365 fprintf(stderr, "^");
2366 }
2367 fprintf(stderr, "\n");
2368 return 1;
2369 }
2370
2371 ssize_t index = mData.indexOfKey(axis);
2372 if (index < 0) {
2373 mData.add(axis, SortedVector<uint32_t>());
2374 }
2375 SortedVector<uint32_t>& sv = mData.editValueFor(axis);
2376 sv.add(value);
2377 // if it's a locale with a region, also match an unmodified locale of the
2378 // same language
2379 if (axis == AXIS_LANGUAGE) {
2380 if (value & 0xffff0000) {
2381 sv.add(value & 0x0000ffff);
2382 }
2383 }
2384 p = q;
2385 if (!*p) break;
2386 p++;
2387 }
2388
2389 return NO_ERROR;
2390}
2391
2392bool
2393ResourceFilter::match(int axis, uint32_t value)
2394{
2395 if (value == 0) {
2396 // they didn't specify anything so take everything
2397 return true;
2398 }
2399 ssize_t index = mData.indexOfKey(axis);
2400 if (index < 0) {
2401 // we didn't request anything on this axis so take everything
2402 return true;
2403 }
2404 const SortedVector<uint32_t>& sv = mData.valueAt(index);
2405 return sv.indexOf(value) >= 0;
2406}
2407
2408bool
2409ResourceFilter::match(const ResTable_config& config)
2410{
2411 if (config.locale) {
2412 uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
2413 | (config.language[1] << 8) | (config.language[0]);
2414 if (!match(AXIS_LANGUAGE, locale)) {
2415 return false;
2416 }
2417 }
2418 if (!match(AXIS_ORIENTATION, config.orientation)) {
2419 return false;
2420 }
2421 if (!match(AXIS_DENSITY, config.density)) {
2422 return false;
2423 }
2424 if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
2425 return false;
2426 }
2427 if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
2428 return false;
2429 }
2430 if (!match(AXIS_KEYBOARD, config.keyboard)) {
2431 return false;
2432 }
2433 if (!match(AXIS_NAVIGATION, config.navigation)) {
2434 return false;
2435 }
2436 if (!match(AXIS_SCREENSIZE, config.screenSize)) {
2437 return false;
2438 }
2439 if (!match(AXIS_VERSION, config.version)) {
2440 return false;
2441 }
2442 return true;
2443}
2444
2445status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
2446{
2447 ResourceFilter filter;
2448 status_t err = filter.parse(bundle->getConfigurations());
2449 if (err != NO_ERROR) {
2450 return err;
2451 }
2452
2453 const size_t N = mOrderedPackages.size();
2454 size_t pi;
2455
2456 // Iterate through all data, collecting all values (strings,
2457 // references, etc).
2458 StringPool valueStrings;
2459 for (pi=0; pi<N; pi++) {
2460 sp<Package> p = mOrderedPackages.itemAt(pi);
2461 if (p->getTypes().size() == 0) {
2462 // Empty, skip!
2463 continue;
2464 }
2465
2466 StringPool typeStrings;
2467 StringPool keyStrings;
2468
2469 const size_t N = p->getOrderedTypes().size();
2470 for (size_t ti=0; ti<N; ti++) {
2471 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2472 if (t == NULL) {
2473 typeStrings.add(String16("<empty>"), false);
2474 continue;
2475 }
2476 typeStrings.add(t->getName(), false);
2477
2478 const size_t N = t->getOrderedConfigs().size();
2479 for (size_t ci=0; ci<N; ci++) {
2480 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2481 if (c == NULL) {
2482 continue;
2483 }
2484 const size_t N = c->getEntries().size();
2485 for (size_t ei=0; ei<N; ei++) {
2486 ConfigDescription config = c->getEntries().keyAt(ei);
2487 if (!filter.match(config)) {
2488 continue;
2489 }
2490 sp<Entry> e = c->getEntries().valueAt(ei);
2491 if (e == NULL) {
2492 continue;
2493 }
2494 e->setNameIndex(keyStrings.add(e->getName(), true));
2495 status_t err = e->prepareFlatten(&valueStrings, this);
2496 if (err != NO_ERROR) {
2497 return err;
2498 }
2499 }
2500 }
2501 }
2502
2503 p->setTypeStrings(typeStrings.createStringBlock());
2504 p->setKeyStrings(keyStrings.createStringBlock());
2505 }
2506
2507 ssize_t strAmt = 0;
2508
2509 // Now build the array of package chunks.
2510 Vector<sp<AaptFile> > flatPackages;
2511 for (pi=0; pi<N; pi++) {
2512 sp<Package> p = mOrderedPackages.itemAt(pi);
2513 if (p->getTypes().size() == 0) {
2514 // Empty, skip!
2515 continue;
2516 }
2517
2518 const size_t N = p->getTypeStrings().size();
2519
2520 const size_t baseSize = sizeof(ResTable_package);
2521
2522 // Start the package data.
2523 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
2524 ResTable_package* header = (ResTable_package*)data->editData(baseSize);
2525 if (header == NULL) {
2526 fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
2527 return NO_MEMORY;
2528 }
2529 memset(header, 0, sizeof(*header));
2530 header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
2531 header->header.headerSize = htods(sizeof(*header));
2532 header->id = htodl(p->getAssignedId());
2533 strcpy16_htod(header->name, p->getName().string());
2534
2535 // Write the string blocks.
2536 const size_t typeStringsStart = data->getSize();
2537 sp<AaptFile> strFile = p->getTypeStringsData();
2538 ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
2539 #if PRINT_STRING_METRICS
2540 fprintf(stderr, "**** type strings: %d\n", amt);
2541 #endif
2542 strAmt += amt;
2543 if (amt < 0) {
2544 return amt;
2545 }
2546 const size_t keyStringsStart = data->getSize();
2547 strFile = p->getKeyStringsData();
2548 amt = data->writeData(strFile->getData(), strFile->getSize());
2549 #if PRINT_STRING_METRICS
2550 fprintf(stderr, "**** key strings: %d\n", amt);
2551 #endif
2552 strAmt += amt;
2553 if (amt < 0) {
2554 return amt;
2555 }
2556
2557 // Build the type chunks inside of this package.
2558 for (size_t ti=0; ti<N; ti++) {
2559 // Retrieve them in the same order as the type string block.
2560 size_t len;
2561 String16 typeName(p->getTypeStrings().stringAt(ti, &len));
2562 sp<Type> t = p->getTypes().valueFor(typeName);
2563 LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
2564 "Type name %s not found",
2565 String8(typeName).string());
2566
2567 const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
2568
2569 // First write the typeSpec chunk, containing information about
2570 // each resource entry in this type.
2571 {
2572 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
2573 const size_t typeSpecStart = data->getSize();
2574 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
2575 (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
2576 if (tsHeader == NULL) {
2577 fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
2578 return NO_MEMORY;
2579 }
2580 memset(tsHeader, 0, sizeof(*tsHeader));
2581 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
2582 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
2583 tsHeader->header.size = htodl(typeSpecSize);
2584 tsHeader->id = ti+1;
2585 tsHeader->entryCount = htodl(N);
2586
2587 uint32_t* typeSpecFlags = (uint32_t*)
2588 (((uint8_t*)data->editData())
2589 + typeSpecStart + sizeof(ResTable_typeSpec));
2590 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
2591
2592 for (size_t ei=0; ei<N; ei++) {
2593 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2594 if (cl->getPublic()) {
2595 typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
2596 }
2597 const size_t CN = cl->getEntries().size();
2598 for (size_t ci=0; ci<CN; ci++) {
2599 if (!filter.match(cl->getEntries().keyAt(ci))) {
2600 continue;
2601 }
2602 for (size_t cj=ci+1; cj<CN; cj++) {
2603 if (!filter.match(cl->getEntries().keyAt(cj))) {
2604 continue;
2605 }
2606 typeSpecFlags[ei] |= htodl(
2607 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
2608 }
2609 }
2610 }
2611 }
2612
2613 // We need to write one type chunk for each configuration for
2614 // which we have entries in this type.
2615 const size_t NC = t->getUniqueConfigs().size();
2616
2617 const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
2618
2619 for (size_t ci=0; ci<NC; ci++) {
2620 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
2621
2622 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
2623 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
2624 ti+1,
2625 config.mcc, config.mnc,
2626 config.language[0] ? config.language[0] : '-',
2627 config.language[1] ? config.language[1] : '-',
2628 config.country[0] ? config.country[0] : '-',
2629 config.country[1] ? config.country[1] : '-',
2630 config.orientation,
2631 config.touchscreen,
2632 config.density,
2633 config.keyboard,
2634 config.inputFlags,
2635 config.navigation,
2636 config.screenWidth,
2637 config.screenHeight));
2638
2639 if (!filter.match(config)) {
2640 continue;
2641 }
2642
2643 const size_t typeStart = data->getSize();
2644
2645 ResTable_type* tHeader = (ResTable_type*)
2646 (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
2647 if (tHeader == NULL) {
2648 fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
2649 return NO_MEMORY;
2650 }
2651
2652 memset(tHeader, 0, sizeof(*tHeader));
2653 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
2654 tHeader->header.headerSize = htods(sizeof(*tHeader));
2655 tHeader->id = ti+1;
2656 tHeader->entryCount = htodl(N);
2657 tHeader->entriesStart = htodl(typeSize);
2658 tHeader->config = config;
2659 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
2660 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
2661 ti+1,
2662 tHeader->config.mcc, tHeader->config.mnc,
2663 tHeader->config.language[0] ? tHeader->config.language[0] : '-',
2664 tHeader->config.language[1] ? tHeader->config.language[1] : '-',
2665 tHeader->config.country[0] ? tHeader->config.country[0] : '-',
2666 tHeader->config.country[1] ? tHeader->config.country[1] : '-',
2667 tHeader->config.orientation,
2668 tHeader->config.touchscreen,
2669 tHeader->config.density,
2670 tHeader->config.keyboard,
2671 tHeader->config.inputFlags,
2672 tHeader->config.navigation,
2673 tHeader->config.screenWidth,
2674 tHeader->config.screenHeight));
2675 tHeader->config.swapHtoD();
2676
2677 // Build the entries inside of this type.
2678 for (size_t ei=0; ei<N; ei++) {
2679 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2680 sp<Entry> e = cl->getEntries().valueFor(config);
2681
2682 // Set the offset for this entry in its type.
2683 uint32_t* index = (uint32_t*)
2684 (((uint8_t*)data->editData())
2685 + typeStart + sizeof(ResTable_type));
2686 if (e != NULL) {
2687 index[ei] = htodl(data->getSize()-typeStart-typeSize);
2688
2689 // Create the entry.
2690 ssize_t amt = e->flatten(bundle, data, cl->getPublic());
2691 if (amt < 0) {
2692 return amt;
2693 }
2694 } else {
2695 index[ei] = htodl(ResTable_type::NO_ENTRY);
2696 }
2697 }
2698
2699 // Fill in the rest of the type information.
2700 tHeader = (ResTable_type*)
2701 (((uint8_t*)data->editData()) + typeStart);
2702 tHeader->header.size = htodl(data->getSize()-typeStart);
2703 }
2704 }
2705
2706 // Fill in the rest of the package information.
2707 header = (ResTable_package*)data->editData();
2708 header->header.size = htodl(data->getSize());
2709 header->typeStrings = htodl(typeStringsStart);
2710 header->lastPublicType = htodl(p->getTypeStrings().size());
2711 header->keyStrings = htodl(keyStringsStart);
2712 header->lastPublicKey = htodl(p->getKeyStrings().size());
2713
2714 flatPackages.add(data);
2715 }
2716
2717 // And now write out the final chunks.
2718 const size_t dataStart = dest->getSize();
2719
2720 {
2721 // blah
2722 ResTable_header header;
2723 memset(&header, 0, sizeof(header));
2724 header.header.type = htods(RES_TABLE_TYPE);
2725 header.header.headerSize = htods(sizeof(header));
2726 header.packageCount = htodl(flatPackages.size());
2727 status_t err = dest->writeData(&header, sizeof(header));
2728 if (err != NO_ERROR) {
2729 fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
2730 return err;
2731 }
2732 }
2733
2734 ssize_t strStart = dest->getSize();
2735 err = valueStrings.writeStringBlock(dest);
2736 if (err != NO_ERROR) {
2737 return err;
2738 }
2739
2740 ssize_t amt = (dest->getSize()-strStart);
2741 strAmt += amt;
2742 #if PRINT_STRING_METRICS
2743 fprintf(stderr, "**** value strings: %d\n", amt);
2744 fprintf(stderr, "**** total strings: %d\n", strAmt);
2745 #endif
2746
2747 for (pi=0; pi<flatPackages.size(); pi++) {
2748 err = dest->writeData(flatPackages[pi]->getData(),
2749 flatPackages[pi]->getSize());
2750 if (err != NO_ERROR) {
2751 fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
2752 return err;
2753 }
2754 }
2755
2756 ResTable_header* header = (ResTable_header*)
2757 (((uint8_t*)dest->getData()) + dataStart);
2758 header->header.size = htodl(dest->getSize() - dataStart);
2759
2760 NOISY(aout << "Resource table:"
2761 << HexDump(dest->getData(), dest->getSize()) << endl);
2762
2763 #if PRINT_STRING_METRICS
2764 fprintf(stderr, "**** total resource table size: %d / %d%% strings\n",
2765 dest->getSize(), (strAmt*100)/dest->getSize());
2766 #endif
2767
2768 return NO_ERROR;
2769}
2770
2771void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
2772{
2773 fprintf(fp,
2774 "<!-- This file contains <public> resource definitions for all\n"
2775 " resources that were generated from the source data. -->\n"
2776 "\n"
2777 "<resources>\n");
2778
2779 writePublicDefinitions(package, fp, true);
2780 writePublicDefinitions(package, fp, false);
2781
2782 fprintf(fp,
2783 "\n"
2784 "</resources>\n");
2785}
2786
2787void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
2788{
2789 bool didHeader = false;
2790
2791 sp<Package> pkg = mPackages.valueFor(package);
2792 if (pkg != NULL) {
2793 const size_t NT = pkg->getOrderedTypes().size();
2794 for (size_t i=0; i<NT; i++) {
2795 sp<Type> t = pkg->getOrderedTypes().itemAt(i);
2796 if (t == NULL) {
2797 continue;
2798 }
2799
2800 bool didType = false;
2801
2802 const size_t NC = t->getOrderedConfigs().size();
2803 for (size_t j=0; j<NC; j++) {
2804 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
2805 if (c == NULL) {
2806 continue;
2807 }
2808
2809 if (c->getPublic() != pub) {
2810 continue;
2811 }
2812
2813 if (!didType) {
2814 fprintf(fp, "\n");
2815 didType = true;
2816 }
2817 if (!didHeader) {
2818 if (pub) {
2819 fprintf(fp," <!-- PUBLIC SECTION. These resources have been declared public.\n");
2820 fprintf(fp," Changes to these definitions will break binary compatibility. -->\n\n");
2821 } else {
2822 fprintf(fp," <!-- PRIVATE SECTION. These resources have not been declared public.\n");
2823 fprintf(fp," You can make them public my moving these lines into a file in res/values. -->\n\n");
2824 }
2825 didHeader = true;
2826 }
2827 if (!pub) {
2828 const size_t NE = c->getEntries().size();
2829 for (size_t k=0; k<NE; k++) {
2830 const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
2831 if (pos.file != "") {
2832 fprintf(fp," <!-- Declared at %s:%d -->\n",
2833 pos.file.string(), pos.line);
2834 }
2835 }
2836 }
2837 fprintf(fp, " <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
2838 String8(t->getName()).string(),
2839 String8(c->getName()).string(),
2840 getResId(pkg, t, c->getEntryIndex()));
2841 }
2842 }
2843 }
2844}
2845
2846ResourceTable::Item::Item(const SourcePos& _sourcePos,
2847 bool _isId,
2848 const String16& _value,
2849 const Vector<StringPool::entry_style_span>* _style,
2850 int32_t _format)
2851 : sourcePos(_sourcePos)
2852 , isId(_isId)
2853 , value(_value)
2854 , format(_format)
2855 , bagKeyId(0)
2856 , evaluating(false)
2857{
2858 if (_style) {
2859 style = *_style;
2860 }
2861}
2862
2863status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
2864{
2865 if (mType == TYPE_BAG) {
2866 return NO_ERROR;
2867 }
2868 if (mType == TYPE_UNKNOWN) {
2869 mType = TYPE_BAG;
2870 return NO_ERROR;
2871 }
2872 sourcePos.error("Resource entry %s is already defined as a single item.\n"
2873 "%s:%d: Originally defined here.\n",
2874 String8(mName).string(),
2875 mItem.sourcePos.file.string(), mItem.sourcePos.line);
2876 return UNKNOWN_ERROR;
2877}
2878
2879status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
2880 const String16& value,
2881 const Vector<StringPool::entry_style_span>* style,
2882 int32_t format,
2883 const bool overwrite)
2884{
2885 Item item(sourcePos, false, value, style);
2886
2887 if (mType == TYPE_BAG) {
2888 const Item& item(mBag.valueAt(0));
2889 sourcePos.error("Resource entry %s is already defined as a bag.\n"
2890 "%s:%d: Originally defined here.\n",
2891 String8(mName).string(),
2892 item.sourcePos.file.string(), item.sourcePos.line);
2893 return UNKNOWN_ERROR;
2894 }
2895 if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
2896 sourcePos.error("Resource entry %s is already defined.\n"
2897 "%s:%d: Originally defined here.\n",
2898 String8(mName).string(),
2899 mItem.sourcePos.file.string(), mItem.sourcePos.line);
2900 return UNKNOWN_ERROR;
2901 }
2902
2903 mType = TYPE_ITEM;
2904 mItem = item;
2905 mItemFormat = format;
2906 return NO_ERROR;
2907}
2908
2909status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
2910 const String16& key, const String16& value,
2911 const Vector<StringPool::entry_style_span>* style,
2912 bool replace, bool isId, int32_t format)
2913{
2914 status_t err = makeItABag(sourcePos);
2915 if (err != NO_ERROR) {
2916 return err;
2917 }
2918
2919 Item item(sourcePos, isId, value, style, format);
2920
2921 // XXX NOTE: there is an error if you try to have a bag with two keys,
2922 // one an attr and one an id, with the same name. Not something we
2923 // currently ever have to worry about.
2924 ssize_t origKey = mBag.indexOfKey(key);
2925 if (origKey >= 0) {
2926 if (!replace) {
2927 const Item& item(mBag.valueAt(origKey));
2928 sourcePos.error("Resource entry %s already has bag item %s.\n"
2929 "%s:%d: Originally defined here.\n",
2930 String8(mName).string(), String8(key).string(),
2931 item.sourcePos.file.string(), item.sourcePos.line);
2932 return UNKNOWN_ERROR;
2933 }
2934 //printf("Replacing %s with %s\n",
2935 // String8(mBag.valueFor(key).value).string(), String8(value).string());
2936 mBag.replaceValueFor(key, item);
2937 }
2938
2939 mBag.add(key, item);
2940 return NO_ERROR;
2941}
2942
Robert Greenwalt4b4f4a92009-04-02 16:55:50 -07002943status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
2944{
2945 status_t err = makeItABag(sourcePos);
2946 if (err != NO_ERROR) {
2947 return err;
2948 }
2949
2950 mBag.clear();
2951 return NO_ERROR;
2952}
2953
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002954status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
2955 const String16& package)
2956{
2957 const String16 attr16("attr");
2958 const String16 id16("id");
2959 const size_t N = mBag.size();
2960 for (size_t i=0; i<N; i++) {
2961 const String16& key = mBag.keyAt(i);
2962 const Item& it = mBag.valueAt(i);
2963 if (it.isId) {
2964 if (!table->hasBagOrEntry(key, &id16, &package)) {
2965 String16 value("false");
2966 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
2967 id16, key, value);
2968 if (err != NO_ERROR) {
2969 return err;
2970 }
2971 }
2972 } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
2973
2974#if 1
2975// fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
2976// String8(key).string());
2977// const Item& item(mBag.valueAt(i));
2978// fprintf(stderr, "Referenced from file %s line %d\n",
2979// item.sourcePos.file.string(), item.sourcePos.line);
2980// return UNKNOWN_ERROR;
2981#else
2982 char numberStr[16];
2983 sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
2984 status_t err = table->addBag(SourcePos("<generated>", 0), package,
2985 attr16, key, String16(""),
2986 String16("^type"),
2987 String16(numberStr), NULL, NULL);
2988 if (err != NO_ERROR) {
2989 return err;
2990 }
2991#endif
2992 }
2993 }
2994 return NO_ERROR;
2995}
2996
2997status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
2998 const String16& package)
2999{
3000 bool hasErrors = false;
3001
3002 if (mType == TYPE_BAG) {
3003 const char* errorMsg;
3004 const String16 style16("style");
3005 const String16 attr16("attr");
3006 const String16 id16("id");
3007 mParentId = 0;
3008 if (mParent.size() > 0) {
3009 mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
3010 if (mParentId == 0) {
3011 mPos.error("Error retrieving parent for item: %s '%s'.\n",
3012 errorMsg, String8(mParent).string());
3013 hasErrors = true;
3014 }
3015 }
3016 const size_t N = mBag.size();
3017 for (size_t i=0; i<N; i++) {
3018 const String16& key = mBag.keyAt(i);
3019 Item& it = mBag.editValueAt(i);
3020 it.bagKeyId = table->getResId(key,
3021 it.isId ? &id16 : &attr16, NULL, &errorMsg);
3022 //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
3023 if (it.bagKeyId == 0) {
3024 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
3025 String8(it.isId ? id16 : attr16).string(),
3026 String8(key).string());
3027 hasErrors = true;
3028 }
3029 }
3030 }
3031 return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
3032}
3033
3034status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
3035{
3036 if (mType == TYPE_ITEM) {
3037 Item& it = mItem;
3038 AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
3039 if (!table->stringToValue(&it.parsedValue, strings,
3040 it.value, false, true, 0,
3041 &it.style, NULL, &ac, mItemFormat)) {
3042 return UNKNOWN_ERROR;
3043 }
3044 } else if (mType == TYPE_BAG) {
3045 const size_t N = mBag.size();
3046 for (size_t i=0; i<N; i++) {
3047 const String16& key = mBag.keyAt(i);
3048 Item& it = mBag.editValueAt(i);
3049 AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
3050 if (!table->stringToValue(&it.parsedValue, strings,
3051 it.value, false, true, it.bagKeyId,
3052 &it.style, NULL, &ac, it.format)) {
3053 return UNKNOWN_ERROR;
3054 }
3055 }
3056 } else {
3057 mPos.error("Error: entry %s is not a single item or a bag.\n",
3058 String8(mName).string());
3059 return UNKNOWN_ERROR;
3060 }
3061 return NO_ERROR;
3062}
3063
3064ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
3065{
3066 size_t amt = 0;
3067 ResTable_entry header;
3068 memset(&header, 0, sizeof(header));
3069 header.size = htods(sizeof(header));
3070 const type ty = this != NULL ? mType : TYPE_ITEM;
3071 if (this != NULL) {
3072 if (ty == TYPE_BAG) {
3073 header.flags |= htods(header.FLAG_COMPLEX);
3074 }
3075 if (isPublic) {
3076 header.flags |= htods(header.FLAG_PUBLIC);
3077 }
3078 header.key.index = htodl(mNameIndex);
3079 }
3080 if (ty != TYPE_BAG) {
3081 status_t err = data->writeData(&header, sizeof(header));
3082 if (err != NO_ERROR) {
3083 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3084 return err;
3085 }
3086
3087 const Item& it = mItem;
3088 Res_value par;
3089 memset(&par, 0, sizeof(par));
3090 par.size = htods(it.parsedValue.size);
3091 par.dataType = it.parsedValue.dataType;
3092 par.res0 = it.parsedValue.res0;
3093 par.data = htodl(it.parsedValue.data);
3094 #if 0
3095 printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
3096 String8(mName).string(), it.parsedValue.dataType,
3097 it.parsedValue.data, par.res0);
3098 #endif
3099 err = data->writeData(&par, it.parsedValue.size);
3100 if (err != NO_ERROR) {
3101 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3102 return err;
3103 }
3104 amt += it.parsedValue.size;
3105 } else {
3106 size_t N = mBag.size();
3107 size_t i;
3108 // Create correct ordering of items.
3109 KeyedVector<uint32_t, const Item*> items;
3110 for (i=0; i<N; i++) {
3111 const Item& it = mBag.valueAt(i);
3112 items.add(it.bagKeyId, &it);
3113 }
3114 N = items.size();
3115
3116 ResTable_map_entry mapHeader;
3117 memcpy(&mapHeader, &header, sizeof(header));
3118 mapHeader.size = htods(sizeof(mapHeader));
3119 mapHeader.parent.ident = htodl(mParentId);
3120 mapHeader.count = htodl(N);
3121 status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
3122 if (err != NO_ERROR) {
3123 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3124 return err;
3125 }
3126
3127 for (i=0; i<N; i++) {
3128 const Item& it = *items.valueAt(i);
3129 ResTable_map map;
3130 map.name.ident = htodl(it.bagKeyId);
3131 map.value.size = htods(it.parsedValue.size);
3132 map.value.dataType = it.parsedValue.dataType;
3133 map.value.res0 = it.parsedValue.res0;
3134 map.value.data = htodl(it.parsedValue.data);
3135 err = data->writeData(&map, sizeof(map));
3136 if (err != NO_ERROR) {
3137 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3138 return err;
3139 }
3140 amt += sizeof(map);
3141 }
3142 }
3143 return amt;
3144}
3145
3146void ResourceTable::ConfigList::appendComment(const String16& comment,
3147 bool onlyIfEmpty)
3148{
3149 if (comment.size() <= 0) {
3150 return;
3151 }
3152 if (onlyIfEmpty && mComment.size() > 0) {
3153 return;
3154 }
3155 if (mComment.size() > 0) {
3156 mComment.append(String16("\n"));
3157 }
3158 mComment.append(comment);
3159}
3160
3161void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
3162{
3163 if (comment.size() <= 0) {
3164 return;
3165 }
3166 if (mTypeComment.size() > 0) {
3167 mTypeComment.append(String16("\n"));
3168 }
3169 mTypeComment.append(comment);
3170}
3171
3172status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
3173 const String16& name,
3174 const uint32_t ident)
3175{
3176 #if 0
3177 int32_t entryIdx = Res_GETENTRY(ident);
3178 if (entryIdx < 0) {
3179 sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
3180 String8(mName).string(), String8(name).string(), ident);
3181 return UNKNOWN_ERROR;
3182 }
3183 #endif
3184
3185 int32_t typeIdx = Res_GETTYPE(ident);
3186 if (typeIdx >= 0) {
3187 typeIdx++;
3188 if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
3189 sourcePos.error("Public resource %s/%s has conflicting type codes for its"
3190 " public identifiers (0x%x vs 0x%x).\n",
3191 String8(mName).string(), String8(name).string(),
3192 mPublicIndex, typeIdx);
3193 return UNKNOWN_ERROR;
3194 }
3195 mPublicIndex = typeIdx;
3196 }
3197
3198 if (mFirstPublicSourcePos == NULL) {
3199 mFirstPublicSourcePos = new SourcePos(sourcePos);
3200 }
3201
3202 if (mPublic.indexOfKey(name) < 0) {
3203 mPublic.add(name, Public(sourcePos, String16(), ident));
3204 } else {
3205 Public& p = mPublic.editValueFor(name);
3206 if (p.ident != ident) {
3207 sourcePos.error("Public resource %s/%s has conflicting public identifiers"
3208 " (0x%08x vs 0x%08x).\n"
3209 "%s:%d: Originally defined here.\n",
3210 String8(mName).string(), String8(name).string(), p.ident, ident,
3211 p.sourcePos.file.string(), p.sourcePos.line);
3212 return UNKNOWN_ERROR;
3213 }
3214 }
3215
3216 return NO_ERROR;
3217}
3218
3219sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
3220 const SourcePos& sourcePos,
3221 const ResTable_config* config,
3222 bool doSetIndex)
3223{
3224 int pos = -1;
3225 sp<ConfigList> c = mConfigs.valueFor(entry);
3226 if (c == NULL) {
3227 c = new ConfigList(entry, sourcePos);
3228 mConfigs.add(entry, c);
3229 pos = (int)mOrderedConfigs.size();
3230 mOrderedConfigs.add(c);
3231 if (doSetIndex) {
3232 c->setEntryIndex(pos);
3233 }
3234 }
3235
3236 ConfigDescription cdesc;
3237 if (config) cdesc = *config;
3238
3239 sp<Entry> e = c->getEntries().valueFor(cdesc);
3240 if (e == NULL) {
3241 if (config != NULL) {
3242 NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
3243 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
3244 sourcePos.file.string(), sourcePos.line,
3245 config->mcc, config->mnc,
3246 config->language[0] ? config->language[0] : '-',
3247 config->language[1] ? config->language[1] : '-',
3248 config->country[0] ? config->country[0] : '-',
3249 config->country[1] ? config->country[1] : '-',
3250 config->orientation,
3251 config->touchscreen,
3252 config->density,
3253 config->keyboard,
3254 config->inputFlags,
3255 config->navigation,
3256 config->screenWidth,
3257 config->screenHeight));
3258 } else {
3259 NOISY(printf("New entry at %s:%d: NULL config\n",
3260 sourcePos.file.string(), sourcePos.line));
3261 }
3262 e = new Entry(entry, sourcePos);
3263 c->addEntry(cdesc, e);
3264 /*
3265 if (doSetIndex) {
3266 if (pos < 0) {
3267 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
3268 if (mOrderedConfigs[pos] == c) {
3269 break;
3270 }
3271 }
3272 if (pos >= (int)mOrderedConfigs.size()) {
3273 sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
3274 return NULL;
3275 }
3276 }
3277 e->setEntryIndex(pos);
3278 }
3279 */
3280 }
3281
3282 mUniqueConfigs.add(cdesc);
3283
3284 return e;
3285}
3286
3287status_t ResourceTable::Type::applyPublicEntryOrder()
3288{
3289 size_t N = mOrderedConfigs.size();
3290 Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
3291 bool hasError = false;
3292
3293 size_t i;
3294 for (i=0; i<N; i++) {
3295 mOrderedConfigs.replaceAt(NULL, i);
3296 }
3297
3298 const size_t NP = mPublic.size();
3299 //printf("Ordering %d configs from %d public defs\n", N, NP);
3300 size_t j;
3301 for (j=0; j<NP; j++) {
3302 const String16& name = mPublic.keyAt(j);
3303 const Public& p = mPublic.valueAt(j);
3304 int32_t idx = Res_GETENTRY(p.ident);
3305 //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
3306 // String8(mName).string(), String8(name).string(), p.ident, N);
3307 bool found = false;
3308 for (i=0; i<N; i++) {
3309 sp<ConfigList> e = origOrder.itemAt(i);
3310 //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
3311 if (e->getName() == name) {
3312 if (idx >= (int32_t)mOrderedConfigs.size()) {
3313 p.sourcePos.error("Public entry identifier 0x%x entry index "
3314 "is larger than available symbols (index %d, total symbols %d).\n",
3315 p.ident, idx, mOrderedConfigs.size());
3316 hasError = true;
3317 } else if (mOrderedConfigs.itemAt(idx) == NULL) {
3318 e->setPublic(true);
3319 e->setPublicSourcePos(p.sourcePos);
3320 mOrderedConfigs.replaceAt(e, idx);
3321 origOrder.removeAt(i);
3322 N--;
3323 found = true;
3324 break;
3325 } else {
3326 sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
3327
3328 p.sourcePos.error("Multiple entry names declared for public entry"
3329 " identifier 0x%x in type %s (%s vs %s).\n"
3330 "%s:%d: Originally defined here.",
3331 idx+1, String8(mName).string(),
3332 String8(oe->getName()).string(),
3333 String8(name).string(),
3334 oe->getPublicSourcePos().file.string(),
3335 oe->getPublicSourcePos().line);
3336 hasError = true;
3337 }
3338 }
3339 }
3340
3341 if (!found) {
3342 p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
3343 String8(mName).string(), String8(name).string());
3344 hasError = true;
3345 }
3346 }
3347
3348 //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
3349
3350 if (N != origOrder.size()) {
3351 printf("Internal error: remaining private symbol count mismatch\n");
3352 N = origOrder.size();
3353 }
3354
3355 j = 0;
3356 for (i=0; i<N; i++) {
3357 sp<ConfigList> e = origOrder.itemAt(i);
3358 // There will always be enough room for the remaining entries.
3359 while (mOrderedConfigs.itemAt(j) != NULL) {
3360 j++;
3361 }
3362 mOrderedConfigs.replaceAt(e, j);
3363 j++;
3364 }
3365
3366 return hasError ? UNKNOWN_ERROR : NO_ERROR;
3367}
3368
3369ResourceTable::Package::Package(const String16& name, ssize_t includedId)
3370 : mName(name), mIncludedId(includedId),
3371 mTypeStringsMapping(0xffffffff),
3372 mKeyStringsMapping(0xffffffff)
3373{
3374}
3375
3376sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
3377 const SourcePos& sourcePos,
3378 bool doSetIndex)
3379{
3380 sp<Type> t = mTypes.valueFor(type);
3381 if (t == NULL) {
3382 t = new Type(type, sourcePos);
3383 mTypes.add(type, t);
3384 mOrderedTypes.add(t);
3385 if (doSetIndex) {
3386 // For some reason the type's index is set to one plus the index
3387 // in the mOrderedTypes list, rather than just the index.
3388 t->setIndex(mOrderedTypes.size());
3389 }
3390 }
3391 return t;
3392}
3393
3394status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
3395{
3396 mTypeStringsData = data;
3397 status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
3398 if (err != NO_ERROR) {
3399 fprintf(stderr, "ERROR: Type string data is corrupt!\n");
3400 }
3401 return err;
3402}
3403
3404status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
3405{
3406 mKeyStringsData = data;
3407 status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
3408 if (err != NO_ERROR) {
3409 fprintf(stderr, "ERROR: Key string data is corrupt!\n");
3410 }
3411 return err;
3412}
3413
3414status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
3415 ResStringPool* strings,
3416 DefaultKeyedVector<String16, uint32_t>* mappings)
3417{
3418 if (data->getData() == NULL) {
3419 return UNKNOWN_ERROR;
3420 }
3421
3422 NOISY(aout << "Setting restable string pool: "
3423 << HexDump(data->getData(), data->getSize()) << endl);
3424
3425 status_t err = strings->setTo(data->getData(), data->getSize());
3426 if (err == NO_ERROR) {
3427 const size_t N = strings->size();
3428 for (size_t i=0; i<N; i++) {
3429 size_t len;
3430 mappings->add(String16(strings->stringAt(i, &len)), i);
3431 }
3432 }
3433 return err;
3434}
3435
3436status_t ResourceTable::Package::applyPublicTypeOrder()
3437{
3438 size_t N = mOrderedTypes.size();
3439 Vector<sp<Type> > origOrder(mOrderedTypes);
3440
3441 size_t i;
3442 for (i=0; i<N; i++) {
3443 mOrderedTypes.replaceAt(NULL, i);
3444 }
3445
3446 for (i=0; i<N; i++) {
3447 sp<Type> t = origOrder.itemAt(i);
3448 int32_t idx = t->getPublicIndex();
3449 if (idx > 0) {
3450 idx--;
3451 while (idx >= (int32_t)mOrderedTypes.size()) {
3452 mOrderedTypes.add();
3453 }
3454 if (mOrderedTypes.itemAt(idx) != NULL) {
3455 sp<Type> ot = mOrderedTypes.itemAt(idx);
3456 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
3457 " identifier 0x%x (%s vs %s).\n"
3458 "%s:%d: Originally defined here.",
3459 idx, String8(ot->getName()).string(),
3460 String8(t->getName()).string(),
3461 ot->getFirstPublicSourcePos().file.string(),
3462 ot->getFirstPublicSourcePos().line);
3463 return UNKNOWN_ERROR;
3464 }
3465 mOrderedTypes.replaceAt(t, idx);
3466 origOrder.removeAt(i);
3467 i--;
3468 N--;
3469 }
3470 }
3471
3472 size_t j=0;
3473 for (i=0; i<N; i++) {
3474 sp<Type> t = origOrder.itemAt(i);
3475 // There will always be enough room for the remaining types.
3476 while (mOrderedTypes.itemAt(j) != NULL) {
3477 j++;
3478 }
3479 mOrderedTypes.replaceAt(t, j);
3480 }
3481
3482 return NO_ERROR;
3483}
3484
3485sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
3486{
3487 sp<Package> p = mPackages.valueFor(package);
3488 if (p == NULL) {
3489 if (mIsAppPackage) {
3490 if (mHaveAppPackage) {
3491 fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
3492 "Use -x to create extended resources.\n");
3493 return NULL;
3494 }
3495 mHaveAppPackage = true;
3496 p = new Package(package, 127);
3497 } else {
3498 p = new Package(package, mNextPackageId);
3499 }
3500 //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
3501 // String8(package).string(), p->getAssignedId());
3502 mPackages.add(package, p);
3503 mOrderedPackages.add(p);
3504 mNextPackageId++;
3505 }
3506 return p;
3507}
3508
3509sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
3510 const String16& type,
3511 const SourcePos& sourcePos,
3512 bool doSetIndex)
3513{
3514 sp<Package> p = getPackage(package);
3515 if (p == NULL) {
3516 return NULL;
3517 }
3518 return p->getType(type, sourcePos, doSetIndex);
3519}
3520
3521sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
3522 const String16& type,
3523 const String16& name,
3524 const SourcePos& sourcePos,
3525 const ResTable_config* config,
3526 bool doSetIndex)
3527{
3528 sp<Type> t = getType(package, type, sourcePos, doSetIndex);
3529 if (t == NULL) {
3530 return NULL;
3531 }
3532 return t->getEntry(name, sourcePos, config, doSetIndex);
3533}
3534
3535sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
3536 const ResTable_config* config) const
3537{
3538 int pid = Res_GETPACKAGE(resID)+1;
3539 const size_t N = mOrderedPackages.size();
3540 size_t i;
3541 sp<Package> p;
3542 for (i=0; i<N; i++) {
3543 sp<Package> check = mOrderedPackages[i];
3544 if (check->getAssignedId() == pid) {
3545 p = check;
3546 break;
3547 }
3548
3549 }
3550 if (p == NULL) {
3551 fprintf(stderr, "WARNING: Package not found for resource #%08x\n", resID);
3552 return NULL;
3553 }
3554
3555 int tid = Res_GETTYPE(resID);
3556 if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
3557 fprintf(stderr, "WARNING: Type not found for resource #%08x\n", resID);
3558 return NULL;
3559 }
3560 sp<Type> t = p->getOrderedTypes()[tid];
3561
3562 int eid = Res_GETENTRY(resID);
3563 if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
3564 fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
3565 return NULL;
3566 }
3567
3568 sp<ConfigList> c = t->getOrderedConfigs()[eid];
3569 if (c == NULL) {
3570 fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
3571 return NULL;
3572 }
3573
3574 ConfigDescription cdesc;
3575 if (config) cdesc = *config;
3576 sp<Entry> e = c->getEntries().valueFor(cdesc);
3577 if (c == NULL) {
3578 fprintf(stderr, "WARNING: Entry configuration not found for resource #%08x\n", resID);
3579 return NULL;
3580 }
3581
3582 return e;
3583}
3584
3585const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
3586{
3587 sp<const Entry> e = getEntry(resID);
3588 if (e == NULL) {
3589 return NULL;
3590 }
3591
3592 const size_t N = e->getBag().size();
3593 for (size_t i=0; i<N; i++) {
3594 const Item& it = e->getBag().valueAt(i);
3595 if (it.bagKeyId == 0) {
3596 fprintf(stderr, "WARNING: ID not yet assigned to '%s' in bag '%s'\n",
3597 String8(e->getName()).string(),
3598 String8(e->getBag().keyAt(i)).string());
3599 }
3600 if (it.bagKeyId == attrID) {
3601 return &it;
3602 }
3603 }
3604
3605 return NULL;
3606}
3607
3608bool ResourceTable::getItemValue(
3609 uint32_t resID, uint32_t attrID, Res_value* outValue)
3610{
3611 const Item* item = getItem(resID, attrID);
3612
3613 bool res = false;
3614 if (item != NULL) {
3615 if (item->evaluating) {
3616 sp<const Entry> e = getEntry(resID);
3617 const size_t N = e->getBag().size();
3618 size_t i;
3619 for (i=0; i<N; i++) {
3620 if (&e->getBag().valueAt(i) == item) {
3621 break;
3622 }
3623 }
3624 fprintf(stderr, "WARNING: Circular reference detected in key '%s' of bag '%s'\n",
3625 String8(e->getName()).string(),
3626 String8(e->getBag().keyAt(i)).string());
3627 return false;
3628 }
3629 item->evaluating = true;
3630 res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
3631 NOISY(
3632 if (res) {
3633 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
3634 resID, attrID, String8(getEntry(resID)->getName()).string(),
3635 outValue->dataType, outValue->data);
3636 } else {
3637 printf("getItemValue of #%08x[#%08x]: failed\n",
3638 resID, attrID);
3639 }
3640 );
3641 item->evaluating = false;
3642 }
3643 return res;
3644}