blob: 66607a33d1f9f535147b7619a8bdbf0b7ae88c4f [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;
732 bool curIsStyled = false;
733 bool curIsPseudolocalizable = false;
734 bool localHasErrors = false;
735
736 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
737 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
738 && code != ResXMLTree::BAD_DOCUMENT) {
739 if (code == ResXMLTree::END_TAG) {
740 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
741 break;
742 }
743 }
744 }
745 continue;
746
747 } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
748 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
749 && code != ResXMLTree::BAD_DOCUMENT) {
750 if (code == ResXMLTree::END_TAG) {
751 if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
752 break;
753 }
754 }
755 }
756 continue;
757
758 } else if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
759 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
760
761 String16 type;
762 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
763 if (typeIdx < 0) {
764 srcPos.error("A 'type' attribute is required for <public>\n");
765 hasErrors = localHasErrors = true;
766 }
767 type = String16(block.getAttributeStringValue(typeIdx, &len));
768
769 String16 name;
770 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
771 if (nameIdx < 0) {
772 srcPos.error("A 'name' attribute is required for <public>\n");
773 hasErrors = localHasErrors = true;
774 }
775 name = String16(block.getAttributeStringValue(nameIdx, &len));
776
777 uint32_t ident = 0;
778 ssize_t identIdx = block.indexOfAttribute(NULL, "id");
779 if (identIdx >= 0) {
780 const char16_t* identStr = block.getAttributeStringValue(identIdx, &len);
781 Res_value identValue;
782 if (!ResTable::stringToInt(identStr, len, &identValue)) {
783 srcPos.error("Given 'id' attribute is not an integer: %s\n",
784 String8(block.getAttributeStringValue(identIdx, &len)).string());
785 hasErrors = localHasErrors = true;
786 } else {
787 ident = identValue.data;
Dianne Hackbornf479aa02009-05-20 16:01:06 -0700788 nextPublicId.replaceValueFor(type, ident+1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 }
Dianne Hackbornf479aa02009-05-20 16:01:06 -0700790 } else if (nextPublicId.indexOfKey(type) < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 srcPos.error("No 'id' attribute supplied <public>,"
792 " and no previous id defined in this file.\n");
793 hasErrors = localHasErrors = true;
794 } else if (!localHasErrors) {
Dianne Hackbornf479aa02009-05-20 16:01:06 -0700795 ident = nextPublicId.valueFor(type);
796 nextPublicId.replaceValueFor(type, ident+1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 }
798
799 if (!localHasErrors) {
800 err = outTable->addPublic(srcPos, myPackage, type, name, ident);
801 if (err < NO_ERROR) {
802 hasErrors = localHasErrors = true;
803 }
804 }
805 if (!localHasErrors) {
806 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
807 if (symbols != NULL) {
808 symbols = symbols->addNestedSymbol(String8(type), srcPos);
809 }
810 if (symbols != NULL) {
811 symbols->makeSymbolPublic(String8(name), srcPos);
812 String16 comment(
813 block.getComment(&len) ? block.getComment(&len) : nulStr);
814 symbols->appendComment(String8(name), comment, srcPos);
815 } else {
816 srcPos.error("Unable to create symbols!\n");
817 hasErrors = localHasErrors = true;
818 }
819 }
820
821 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
822 if (code == ResXMLTree::END_TAG) {
823 if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
824 break;
825 }
826 }
827 }
828 continue;
829
Dianne Hackbornf479aa02009-05-20 16:01:06 -0700830 } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
831 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
832
833 String16 type;
834 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
835 if (typeIdx < 0) {
836 srcPos.error("A 'type' attribute is required for <public-padding>\n");
837 hasErrors = localHasErrors = true;
838 }
839 type = String16(block.getAttributeStringValue(typeIdx, &len));
840
841 String16 name;
842 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
843 if (nameIdx < 0) {
844 srcPos.error("A 'name' attribute is required for <public-padding>\n");
845 hasErrors = localHasErrors = true;
846 }
847 name = String16(block.getAttributeStringValue(nameIdx, &len));
848
849 uint32_t start = 0;
850 ssize_t startIdx = block.indexOfAttribute(NULL, "start");
851 if (startIdx >= 0) {
852 const char16_t* startStr = block.getAttributeStringValue(startIdx, &len);
853 Res_value startValue;
854 if (!ResTable::stringToInt(startStr, len, &startValue)) {
855 srcPos.error("Given 'start' attribute is not an integer: %s\n",
856 String8(block.getAttributeStringValue(startIdx, &len)).string());
857 hasErrors = localHasErrors = true;
858 } else {
859 start = startValue.data;
860 }
861 } else if (nextPublicId.indexOfKey(type) < 0) {
862 srcPos.error("No 'start' attribute supplied <public-padding>,"
863 " and no previous id defined in this file.\n");
864 hasErrors = localHasErrors = true;
865 } else if (!localHasErrors) {
866 start = nextPublicId.valueFor(type);
867 }
868
869 uint32_t end = 0;
870 ssize_t endIdx = block.indexOfAttribute(NULL, "end");
871 if (endIdx >= 0) {
872 const char16_t* endStr = block.getAttributeStringValue(endIdx, &len);
873 Res_value endValue;
874 if (!ResTable::stringToInt(endStr, len, &endValue)) {
875 srcPos.error("Given 'end' attribute is not an integer: %s\n",
876 String8(block.getAttributeStringValue(endIdx, &len)).string());
877 hasErrors = localHasErrors = true;
878 } else {
879 end = endValue.data;
880 }
881 } else {
882 srcPos.error("No 'end' attribute supplied <public-padding>\n");
883 hasErrors = localHasErrors = true;
884 }
885
886 if (end >= start) {
887 nextPublicId.replaceValueFor(type, end+1);
888 } else {
889 srcPos.error("Padding start '%ul' is after end '%ul'\n",
890 start, end);
891 hasErrors = localHasErrors = true;
892 }
893
894 String16 comment(
895 block.getComment(&len) ? block.getComment(&len) : nulStr);
896 for (uint32_t curIdent=start; curIdent<=end; curIdent++) {
897 if (localHasErrors) {
898 break;
899 }
900 String16 curName(name);
901 char buf[64];
902 sprintf(buf, "%d", (int)(end-curIdent+1));
903 curName.append(String16(buf));
904
905 err = outTable->addEntry(srcPos, myPackage, type, curName,
906 String16("padding"), NULL, &curParams, false,
907 ResTable_map::TYPE_STRING, overwrite);
908 if (err < NO_ERROR) {
909 hasErrors = localHasErrors = true;
910 break;
911 }
912 err = outTable->addPublic(srcPos, myPackage, type,
913 curName, curIdent);
914 if (err < NO_ERROR) {
915 hasErrors = localHasErrors = true;
916 break;
917 }
918 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
919 if (symbols != NULL) {
920 symbols = symbols->addNestedSymbol(String8(type), srcPos);
921 }
922 if (symbols != NULL) {
923 symbols->makeSymbolPublic(String8(curName), srcPos);
924 symbols->appendComment(String8(curName), comment, srcPos);
925 } else {
926 srcPos.error("Unable to create symbols!\n");
927 hasErrors = localHasErrors = true;
928 }
929 }
930
931 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
932 if (code == ResXMLTree::END_TAG) {
933 if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
934 break;
935 }
936 }
937 }
938 continue;
939
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
941 String16 pkg;
942 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
943 if (pkgIdx < 0) {
944 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
945 "A 'package' attribute is required for <private-symbols>\n");
946 hasErrors = localHasErrors = true;
947 }
948 pkg = String16(block.getAttributeStringValue(pkgIdx, &len));
949 if (!localHasErrors) {
950 assets->setSymbolsPrivatePackage(String8(pkg));
951 }
952
953 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
954 if (code == ResXMLTree::END_TAG) {
955 if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
956 break;
957 }
958 }
959 }
960 continue;
961
962 } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
963 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
964
965 String16 ident;
966 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
967 if (identIdx < 0) {
968 srcPos.error("A 'name' attribute is required for <declare-styleable>\n");
969 hasErrors = localHasErrors = true;
970 }
971 ident = String16(block.getAttributeStringValue(identIdx, &len));
972
973 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
974 if (!localHasErrors) {
975 if (symbols != NULL) {
976 symbols = symbols->addNestedSymbol(String8("styleable"), srcPos);
977 }
978 sp<AaptSymbols> styleSymbols = symbols;
979 if (symbols != NULL) {
980 symbols = symbols->addNestedSymbol(String8(ident), srcPos);
981 }
982 if (symbols == NULL) {
983 srcPos.error("Unable to create symbols!\n");
984 return UNKNOWN_ERROR;
985 }
986
987 String16 comment(
988 block.getComment(&len) ? block.getComment(&len) : nulStr);
989 styleSymbols->appendComment(String8(ident), comment, srcPos);
990 } else {
991 symbols = NULL;
992 }
993
994 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
995 if (code == ResXMLTree::START_TAG) {
996 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
997 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
998 && code != ResXMLTree::BAD_DOCUMENT) {
999 if (code == ResXMLTree::END_TAG) {
1000 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
1001 break;
1002 }
1003 }
1004 }
1005 continue;
1006 } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
1007 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1008 && code != ResXMLTree::BAD_DOCUMENT) {
1009 if (code == ResXMLTree::END_TAG) {
1010 if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
1011 break;
1012 }
1013 }
1014 }
1015 continue;
1016 } else if (strcmp16(block.getElementName(&len), attr16.string()) != 0) {
1017 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1018 "Tag <%s> can not appear inside <declare-styleable>, only <attr>\n",
1019 String8(block.getElementName(&len)).string());
1020 return UNKNOWN_ERROR;
1021 }
1022
1023 String16 comment(
1024 block.getComment(&len) ? block.getComment(&len) : nulStr);
1025 String16 itemIdent;
1026 err = compileAttribute(in, block, myPackage, outTable, &itemIdent, true);
1027 if (err != NO_ERROR) {
1028 hasErrors = localHasErrors = true;
1029 }
1030
1031 if (symbols != NULL) {
1032 SourcePos srcPos(String8(in->getPrintableSource()), block.getLineNumber());
1033 symbols->addSymbol(String8(itemIdent), 0, srcPos);
1034 symbols->appendComment(String8(itemIdent), comment, srcPos);
1035 //printf("Attribute %s comment: %s\n", String8(itemIdent).string(),
1036 // String8(comment).string());
1037 }
1038 } else if (code == ResXMLTree::END_TAG) {
1039 if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
1040 break;
1041 }
1042
1043 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1044 "Found tag </%s> where </attr> is expected\n",
1045 String8(block.getElementName(&len)).string());
1046 return UNKNOWN_ERROR;
1047 }
1048 }
1049 continue;
1050
1051 } else if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
1052 err = compileAttribute(in, block, myPackage, outTable, NULL);
1053 if (err != NO_ERROR) {
1054 hasErrors = true;
1055 }
1056 continue;
1057
1058 } else if (strcmp16(block.getElementName(&len), item16.string()) == 0) {
1059 curTag = &item16;
1060 ssize_t attri = block.indexOfAttribute(NULL, "type");
1061 if (attri >= 0) {
1062 curType = String16(block.getAttributeStringValue(attri, &len));
1063 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
1064 if (formatIdx >= 0) {
1065 String16 formatStr = String16(block.getAttributeStringValue(
1066 formatIdx, &len));
1067 curFormat = parse_flags(formatStr.string(), formatStr.size(),
1068 gFormatFlags);
1069 if (curFormat == 0) {
1070 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1071 "Tag <item> 'format' attribute value \"%s\" not valid\n",
1072 String8(formatStr).string());
1073 hasErrors = localHasErrors = true;
1074 }
1075 }
1076 } else {
1077 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1078 "A 'type' attribute is required for <item>\n");
1079 hasErrors = localHasErrors = true;
1080 }
1081 curIsStyled = true;
1082 } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
1083 // Note the existence and locale of every string we process
1084 char rawLocale[16];
1085 curParams.getLocale(rawLocale);
1086 String8 locale(rawLocale);
1087 String16 name;
1088 String16 translatable;
1089
1090 size_t n = block.getAttributeCount();
1091 for (size_t i = 0; i < n; i++) {
1092 size_t length;
1093 const uint16_t* attr = block.getAttributeName(i, &length);
1094 if (strcmp16(attr, name16.string()) == 0) {
1095 name.setTo(block.getAttributeStringValue(i, &length));
1096 } else if (strcmp16(attr, translatable16.string()) == 0) {
1097 translatable.setTo(block.getAttributeStringValue(i, &length));
1098 }
1099 }
1100
1101 if (name.size() > 0) {
1102 if (translatable == false16) {
1103 // Untranslatable strings must only exist in the default [empty] locale
1104 if (locale.size() > 0) {
1105 fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
1106 " in locale '%s'\n", String8(name).string(),
1107 bundle->getResourceSourceDirs()[0],
1108 locale.string());
1109 // hasErrors = localHasErrors = true;
1110 } else {
1111 // Intentionally empty block:
1112 //
1113 // Don't add untranslatable strings to the localization table; that
1114 // way if we later see localizations of them, they'll be flagged as
1115 // having no default translation.
1116 }
1117 } else {
1118 outTable->addLocalization(name, locale);
1119 }
1120 }
1121
1122 curTag = &string16;
1123 curType = string16;
1124 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
1125 curIsStyled = true;
1126 curIsPseudolocalizable = true;
1127 } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) {
1128 curTag = &drawable16;
1129 curType = drawable16;
1130 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
1131 } else if (strcmp16(block.getElementName(&len), color16.string()) == 0) {
1132 curTag = &color16;
1133 curType = color16;
1134 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
1135 } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
1136 curTag = &bool16;
1137 curType = bool16;
1138 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
1139 } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
1140 curTag = &integer16;
1141 curType = integer16;
1142 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
1143 } else if (strcmp16(block.getElementName(&len), dimen16.string()) == 0) {
1144 curTag = &dimen16;
1145 curType = dimen16;
1146 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
1147 } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
1148 curTag = &fraction16;
1149 curType = fraction16;
1150 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
1151 } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
1152 curTag = &bag16;
1153 curIsBag = true;
1154 ssize_t attri = block.indexOfAttribute(NULL, "type");
1155 if (attri >= 0) {
1156 curType = String16(block.getAttributeStringValue(attri, &len));
1157 } else {
1158 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1159 "A 'type' attribute is required for <bag>\n");
1160 hasErrors = localHasErrors = true;
1161 }
1162 } else if (strcmp16(block.getElementName(&len), style16.string()) == 0) {
1163 curTag = &style16;
1164 curType = style16;
1165 curIsBag = true;
1166 } else if (strcmp16(block.getElementName(&len), plurals16.string()) == 0) {
1167 curTag = &plurals16;
1168 curType = plurals16;
1169 curIsBag = true;
1170 } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) {
1171 curTag = &array16;
1172 curType = array16;
1173 curIsBag = true;
1174 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
1175 if (formatIdx >= 0) {
1176 String16 formatStr = String16(block.getAttributeStringValue(
1177 formatIdx, &len));
1178 curFormat = parse_flags(formatStr.string(), formatStr.size(),
1179 gFormatFlags);
1180 if (curFormat == 0) {
1181 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1182 "Tag <array> 'format' attribute value \"%s\" not valid\n",
1183 String8(formatStr).string());
1184 hasErrors = localHasErrors = true;
1185 }
1186 }
1187 } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
1188 curTag = &string_array16;
1189 curType = array16;
1190 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
1191 curIsBag = true;
1192 curIsPseudolocalizable = true;
1193 } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
1194 curTag = &integer_array16;
1195 curType = array16;
1196 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
1197 curIsBag = true;
1198 } else {
1199 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1200 "Found tag %s where item is expected\n",
1201 String8(block.getElementName(&len)).string());
1202 return UNKNOWN_ERROR;
1203 }
1204
1205 String16 ident;
1206 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
1207 if (identIdx >= 0) {
1208 ident = String16(block.getAttributeStringValue(identIdx, &len));
1209 } else {
1210 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1211 "A 'name' attribute is required for <%s>\n",
1212 String8(*curTag).string());
1213 hasErrors = localHasErrors = true;
1214 }
1215
1216 String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr);
1217
1218 if (curIsBag) {
1219 // Figure out the parent of this bag...
1220 String16 parentIdent;
1221 ssize_t parentIdentIdx = block.indexOfAttribute(NULL, "parent");
1222 if (parentIdentIdx >= 0) {
1223 parentIdent = String16(block.getAttributeStringValue(parentIdentIdx, &len));
1224 } else {
1225 ssize_t sep = ident.findLast('.');
1226 if (sep >= 0) {
1227 parentIdent.setTo(ident, sep);
1228 }
1229 }
1230
1231 if (!localHasErrors) {
1232 err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
Robert Greenwalt4b4f4a92009-04-02 16:55:50 -07001233 myPackage, curType, ident, parentIdent, &curParams,
1234 overwrite);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001235 if (err != NO_ERROR) {
1236 hasErrors = localHasErrors = true;
1237 }
1238 }
1239
1240 ssize_t elmIndex = 0;
1241 char elmIndexStr[14];
1242 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1243 && code != ResXMLTree::BAD_DOCUMENT) {
1244
1245 if (code == ResXMLTree::START_TAG) {
1246 if (strcmp16(block.getElementName(&len), item16.string()) != 0) {
1247 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1248 "Tag <%s> can not appear inside <%s>, only <item>\n",
1249 String8(block.getElementName(&len)).string(),
1250 String8(*curTag).string());
1251 return UNKNOWN_ERROR;
1252 }
1253
1254 String16 itemIdent;
1255 if (curType == array16) {
1256 sprintf(elmIndexStr, "^index_%d", (int)elmIndex++);
1257 itemIdent = String16(elmIndexStr);
1258 } else if (curType == plurals16) {
1259 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "quantity");
1260 if (itemIdentIdx >= 0) {
1261 String16 quantity16(block.getAttributeStringValue(itemIdentIdx, &len));
1262 if (quantity16 == other16) {
1263 itemIdent = quantityOther16;
1264 }
1265 else if (quantity16 == zero16) {
1266 itemIdent = quantityZero16;
1267 }
1268 else if (quantity16 == one16) {
1269 itemIdent = quantityOne16;
1270 }
1271 else if (quantity16 == two16) {
1272 itemIdent = quantityTwo16;
1273 }
1274 else if (quantity16 == few16) {
1275 itemIdent = quantityFew16;
1276 }
1277 else if (quantity16 == many16) {
1278 itemIdent = quantityMany16;
1279 }
1280 else {
1281 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1282 "Illegal 'quantity' attribute is <item> inside <plurals>\n");
1283 hasErrors = localHasErrors = true;
1284 }
1285 } else {
1286 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1287 "A 'quantity' attribute is required for <item> inside <plurals>\n");
1288 hasErrors = localHasErrors = true;
1289 }
1290 } else {
1291 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
1292 if (itemIdentIdx >= 0) {
1293 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
1294 } else {
1295 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1296 "A 'name' attribute is required for <item>\n");
1297 hasErrors = localHasErrors = true;
1298 }
1299 }
1300
1301 ResXMLParser::ResXMLPosition parserPosition;
1302 block.getPosition(&parserPosition);
1303
1304 err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
1305 ident, parentIdent, itemIdent, curFormat,
1306 false, overwrite, outTable);
1307 if (err == NO_ERROR) {
1308 if (curIsPseudolocalizable && localeIsDefined(curParams)
1309 && bundle->getPseudolocalize()) {
1310 // pseudolocalize here
1311#if 1
1312 block.setPosition(parserPosition);
1313 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
1314 curType, ident, parentIdent, itemIdent, curFormat, true,
1315 overwrite, outTable);
1316#endif
1317 }
1318 }
1319 if (err != NO_ERROR) {
1320 hasErrors = localHasErrors = true;
1321 }
1322 } else if (code == ResXMLTree::END_TAG) {
1323 if (strcmp16(block.getElementName(&len), curTag->string()) != 0) {
1324 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1325 "Found tag </%s> where </%s> is expected\n",
1326 String8(block.getElementName(&len)).string(),
1327 String8(*curTag).string());
1328 return UNKNOWN_ERROR;
1329 }
1330 break;
1331 }
1332 }
1333 } else {
1334 ResXMLParser::ResXMLPosition parserPosition;
1335 block.getPosition(&parserPosition);
1336
1337 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
1338 *curTag, curIsStyled, curFormat, false, overwrite, outTable);
1339
1340 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
1341 hasErrors = localHasErrors = true;
1342 }
1343 else if (err == NO_ERROR) {
1344 if (curIsPseudolocalizable && localeIsDefined(curParams)
1345 && bundle->getPseudolocalize()) {
1346 // pseudolocalize here
1347 block.setPosition(parserPosition);
1348 err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
Robert Greenwalt32c2c902009-05-08 11:45:37 -07001349 ident, *curTag, curIsStyled, curFormat, true, overwrite, outTable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 if (err != NO_ERROR) {
1351 hasErrors = localHasErrors = true;
1352 }
1353 }
1354 }
1355 }
1356
1357#if 0
1358 if (comment.size() > 0) {
1359 printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).string(),
1360 String8(curType).string(), String8(ident).string(),
1361 String8(comment).string());
1362 }
1363#endif
1364 if (!localHasErrors) {
1365 outTable->appendComment(myPackage, curType, ident, comment, false);
1366 }
1367 }
1368 else if (code == ResXMLTree::END_TAG) {
1369 if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
1370 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1371 "Unexpected end tag %s\n", String8(block.getElementName(&len)).string());
1372 return UNKNOWN_ERROR;
1373 }
1374 }
1375 else if (code == ResXMLTree::START_NAMESPACE || code == ResXMLTree::END_NAMESPACE) {
1376 }
1377 else if (code == ResXMLTree::TEXT) {
1378 if (isWhitespace(block.getText(&len))) {
1379 continue;
1380 }
1381 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1382 "Found text \"%s\" where item tag is expected\n",
1383 String8(block.getText(&len)).string());
1384 return UNKNOWN_ERROR;
1385 }
1386 }
1387
1388 return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
1389}
1390
1391ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage)
1392 : mAssetsPackage(assetsPackage), mNextPackageId(1), mHaveAppPackage(false),
1393 mIsAppPackage(!bundle->getExtending()),
1394 mNumLocal(0),
1395 mBundle(bundle)
1396{
1397}
1398
1399status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets)
1400{
1401 status_t err = assets->buildIncludedResources(bundle);
1402 if (err != NO_ERROR) {
1403 return err;
1404 }
1405
1406 // For future reference to included resources.
1407 mAssets = assets;
1408
1409 const ResTable& incl = assets->getIncludedResources();
1410
1411 // Retrieve all the packages.
1412 const size_t N = incl.getBasePackageCount();
1413 for (size_t phase=0; phase<2; phase++) {
1414 for (size_t i=0; i<N; i++) {
1415 String16 name(incl.getBasePackageName(i));
1416 uint32_t id = incl.getBasePackageId(i);
1417 // First time through: only add base packages (id
1418 // is not 0); second time through add the other
1419 // packages.
1420 if (phase != 0) {
1421 if (id != 0) {
1422 // Skip base packages -- already one.
1423 id = 0;
1424 } else {
1425 // Assign a dynamic id.
1426 id = mNextPackageId;
1427 }
1428 } else if (id != 0) {
1429 if (id == 127) {
1430 if (mHaveAppPackage) {
Dianne Hackborna96cbb42009-05-13 15:06:13 -07001431 fprintf(stderr, "Included resources have two application packages!\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 return UNKNOWN_ERROR;
1433 }
1434 mHaveAppPackage = true;
1435 }
1436 if (mNextPackageId > id) {
1437 fprintf(stderr, "Included base package ID %d already in use!\n", id);
1438 return UNKNOWN_ERROR;
1439 }
1440 }
1441 if (id != 0) {
1442 NOISY(printf("Including package %s with ID=%d\n",
1443 String8(name).string(), id));
1444 sp<Package> p = new Package(name, id);
1445 mPackages.add(name, p);
1446 mOrderedPackages.add(p);
1447
1448 if (id >= mNextPackageId) {
1449 mNextPackageId = id+1;
1450 }
1451 }
1452 }
1453 }
1454
1455 // Every resource table always has one first entry, the bag attributes.
1456 const SourcePos unknown(String8("????"), 0);
1457 sp<Type> attr = getType(mAssetsPackage, String16("attr"), unknown);
1458
1459 return NO_ERROR;
1460}
1461
1462status_t ResourceTable::addPublic(const SourcePos& sourcePos,
1463 const String16& package,
1464 const String16& type,
1465 const String16& name,
1466 const uint32_t ident)
1467{
1468 uint32_t rid = mAssets->getIncludedResources()
1469 .identifierForName(name.string(), name.size(),
1470 type.string(), type.size(),
1471 package.string(), package.size());
1472 if (rid != 0) {
1473 sourcePos.error("Error declaring public resource %s/%s for included package %s\n",
1474 String8(type).string(), String8(name).string(),
1475 String8(package).string());
1476 return UNKNOWN_ERROR;
1477 }
1478
1479 sp<Type> t = getType(package, type, sourcePos);
1480 if (t == NULL) {
1481 return UNKNOWN_ERROR;
1482 }
1483 return t->addPublic(sourcePos, name, ident);
1484}
1485
1486status_t ResourceTable::addEntry(const SourcePos& sourcePos,
1487 const String16& package,
1488 const String16& type,
1489 const String16& name,
1490 const String16& value,
1491 const Vector<StringPool::entry_style_span>* style,
1492 const ResTable_config* params,
1493 const bool doSetIndex,
1494 const int32_t format,
1495 const bool overwrite)
1496{
1497 // Check for adding entries in other packages... for now we do
1498 // nothing. We need to do the right thing here to support skinning.
1499 uint32_t rid = mAssets->getIncludedResources()
1500 .identifierForName(name.string(), name.size(),
1501 type.string(), type.size(),
1502 package.string(), package.size());
1503 if (rid != 0) {
1504 return NO_ERROR;
1505 }
1506
1507#if 0
1508 if (name == String16("left")) {
1509 printf("Adding entry left: file=%s, line=%d, type=%s, value=%s\n",
1510 sourcePos.file.string(), sourcePos.line, String8(type).string(),
1511 String8(value).string());
1512 }
1513#endif
1514
1515 sp<Entry> e = getEntry(package, type, name, sourcePos, params, doSetIndex);
1516 if (e == NULL) {
1517 return UNKNOWN_ERROR;
1518 }
1519 status_t err = e->setItem(sourcePos, value, style, format, overwrite);
1520 if (err == NO_ERROR) {
1521 mNumLocal++;
1522 }
1523 return err;
1524}
1525
1526status_t ResourceTable::startBag(const SourcePos& sourcePos,
1527 const String16& package,
1528 const String16& type,
1529 const String16& name,
1530 const String16& bagParent,
1531 const ResTable_config* params,
1532 bool replace, bool isId)
1533{
Robert Greenwalt4b4f4a92009-04-02 16:55:50 -07001534 status_t result = NO_ERROR;
1535
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 // Check for adding entries in other packages... for now we do
1537 // nothing. We need to do the right thing here to support skinning.
1538 uint32_t rid = mAssets->getIncludedResources()
1539 .identifierForName(name.string(), name.size(),
1540 type.string(), type.size(),
1541 package.string(), package.size());
1542 if (rid != 0) {
1543 return NO_ERROR;
1544 }
1545
1546#if 0
1547 if (name == String16("left")) {
1548 printf("Adding bag left: file=%s, line=%d, type=%s\n",
1549 sourcePos.file.striing(), sourcePos.line, String8(type).string());
1550 }
1551#endif
1552
1553 sp<Entry> e = getEntry(package, type, name, sourcePos, params);
1554 if (e == NULL) {
1555 return UNKNOWN_ERROR;
1556 }
1557
1558 // If a parent is explicitly specified, set it.
1559 if (bagParent.size() > 0) {
1560 String16 curPar = e->getParent();
1561 if (curPar.size() > 0 && curPar != bagParent) {
1562 sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
1563 String8(e->getParent()).string(),
1564 String8(bagParent).string());
1565 return UNKNOWN_ERROR;
1566 }
1567 e->setParent(bagParent);
1568 }
Robert Greenwalt4b4f4a92009-04-02 16:55:50 -07001569
1570 if ((result = e->makeItABag(sourcePos)) != NO_ERROR) {
1571 return result;
1572 }
Robert Greenwalt9411a392009-04-03 16:44:30 -07001573
1574 if (replace) {
1575 return e->emptyBag(sourcePos);
1576 }
1577 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001578}
1579
1580status_t ResourceTable::addBag(const SourcePos& sourcePos,
1581 const String16& package,
1582 const String16& type,
1583 const String16& name,
1584 const String16& bagParent,
1585 const String16& bagKey,
1586 const String16& value,
1587 const Vector<StringPool::entry_style_span>* style,
1588 const ResTable_config* params,
1589 bool replace, bool isId, const int32_t format)
1590{
1591 // Check for adding entries in other packages... for now we do
1592 // nothing. We need to do the right thing here to support skinning.
1593 uint32_t rid = mAssets->getIncludedResources()
1594 .identifierForName(name.string(), name.size(),
1595 type.string(), type.size(),
1596 package.string(), package.size());
1597 if (rid != 0) {
1598 return NO_ERROR;
1599 }
1600
1601#if 0
1602 if (name == String16("left")) {
1603 printf("Adding bag left: file=%s, line=%d, type=%s\n",
1604 sourcePos.file.striing(), sourcePos.line, String8(type).string());
1605 }
1606#endif
1607
1608 sp<Entry> e = getEntry(package, type, name, sourcePos, params);
1609 if (e == NULL) {
1610 return UNKNOWN_ERROR;
1611 }
1612
1613 // If a parent is explicitly specified, set it.
1614 if (bagParent.size() > 0) {
1615 String16 curPar = e->getParent();
1616 if (curPar.size() > 0 && curPar != bagParent) {
1617 sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
1618 String8(e->getParent()).string(),
1619 String8(bagParent).string());
1620 return UNKNOWN_ERROR;
1621 }
1622 e->setParent(bagParent);
1623 }
1624
1625 const bool first = e->getBag().indexOfKey(bagKey) < 0;
1626 status_t err = e->addToBag(sourcePos, bagKey, value, style, replace, isId, format);
1627 if (err == NO_ERROR && first) {
1628 mNumLocal++;
1629 }
1630 return err;
1631}
1632
1633bool ResourceTable::hasBagOrEntry(const String16& package,
1634 const String16& type,
1635 const String16& name) const
1636{
1637 // First look for this in the included resources...
1638 uint32_t rid = mAssets->getIncludedResources()
1639 .identifierForName(name.string(), name.size(),
1640 type.string(), type.size(),
1641 package.string(), package.size());
1642 if (rid != 0) {
1643 return true;
1644 }
1645
1646 sp<Package> p = mPackages.valueFor(package);
1647 if (p != NULL) {
1648 sp<Type> t = p->getTypes().valueFor(type);
1649 if (t != NULL) {
1650 sp<ConfigList> c = t->getConfigs().valueFor(name);
1651 if (c != NULL) return true;
1652 }
1653 }
1654
1655 return false;
1656}
1657
1658bool ResourceTable::hasBagOrEntry(const String16& ref,
1659 const String16* defType,
1660 const String16* defPackage)
1661{
1662 String16 package, type, name;
1663 if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
1664 defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
1665 return false;
1666 }
1667 return hasBagOrEntry(package, type, name);
1668}
1669
1670bool ResourceTable::appendComment(const String16& package,
1671 const String16& type,
1672 const String16& name,
1673 const String16& comment,
1674 bool onlyIfEmpty)
1675{
1676 if (comment.size() <= 0) {
1677 return true;
1678 }
1679
1680 sp<Package> p = mPackages.valueFor(package);
1681 if (p != NULL) {
1682 sp<Type> t = p->getTypes().valueFor(type);
1683 if (t != NULL) {
1684 sp<ConfigList> c = t->getConfigs().valueFor(name);
1685 if (c != NULL) {
1686 c->appendComment(comment, onlyIfEmpty);
1687 return true;
1688 }
1689 }
1690 }
1691 return false;
1692}
1693
1694bool ResourceTable::appendTypeComment(const String16& package,
1695 const String16& type,
1696 const String16& name,
1697 const String16& comment)
1698{
1699 if (comment.size() <= 0) {
1700 return true;
1701 }
1702
1703 sp<Package> p = mPackages.valueFor(package);
1704 if (p != NULL) {
1705 sp<Type> t = p->getTypes().valueFor(type);
1706 if (t != NULL) {
1707 sp<ConfigList> c = t->getConfigs().valueFor(name);
1708 if (c != NULL) {
1709 c->appendTypeComment(comment);
1710 return true;
1711 }
1712 }
1713 }
1714 return false;
1715}
1716
1717size_t ResourceTable::size() const {
1718 return mPackages.size();
1719}
1720
1721size_t ResourceTable::numLocalResources() const {
1722 return mNumLocal;
1723}
1724
1725bool ResourceTable::hasResources() const {
1726 return mNumLocal > 0;
1727}
1728
1729sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
1730{
1731 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
1732 status_t err = flatten(bundle, data);
1733 return err == NO_ERROR ? data : NULL;
1734}
1735
1736inline uint32_t ResourceTable::getResId(const sp<Package>& p,
1737 const sp<Type>& t,
1738 uint32_t nameId)
1739{
1740 return makeResId(p->getAssignedId(), t->getIndex(), nameId);
1741}
1742
1743uint32_t ResourceTable::getResId(const String16& package,
1744 const String16& type,
1745 const String16& name,
1746 bool onlyPublic) const
1747{
1748 sp<Package> p = mPackages.valueFor(package);
1749 if (p == NULL) return 0;
1750
1751 // First look for this in the included resources...
1752 uint32_t specFlags = 0;
1753 uint32_t rid = mAssets->getIncludedResources()
1754 .identifierForName(name.string(), name.size(),
1755 type.string(), type.size(),
1756 package.string(), package.size(),
1757 &specFlags);
1758 if (rid != 0) {
1759 if (onlyPublic) {
1760 if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
1761 return 0;
1762 }
1763 }
1764
1765 if (Res_INTERNALID(rid)) {
1766 return rid;
1767 }
1768 return Res_MAKEID(p->getAssignedId()-1,
1769 Res_GETTYPE(rid),
1770 Res_GETENTRY(rid));
1771 }
1772
1773 sp<Type> t = p->getTypes().valueFor(type);
1774 if (t == NULL) return 0;
1775 sp<ConfigList> c = t->getConfigs().valueFor(name);
1776 if (c == NULL) return 0;
1777 int32_t ei = c->getEntryIndex();
1778 if (ei < 0) return 0;
1779 return getResId(p, t, ei);
1780}
1781
1782uint32_t ResourceTable::getResId(const String16& ref,
1783 const String16* defType,
1784 const String16* defPackage,
1785 const char** outErrorMsg,
1786 bool onlyPublic) const
1787{
1788 String16 package, type, name;
1789 if (!ResTable::expandResourceRef(
1790 ref.string(), ref.size(), &package, &type, &name,
1791 defType, defPackage ? defPackage:&mAssetsPackage,
1792 outErrorMsg)) {
1793 NOISY(printf("Expanding resource: ref=%s\n",
1794 String8(ref).string()));
1795 NOISY(printf("Expanding resource: defType=%s\n",
1796 defType ? String8(*defType).string() : "NULL"));
1797 NOISY(printf("Expanding resource: defPackage=%s\n",
1798 defPackage ? String8(*defPackage).string() : "NULL"));
1799 NOISY(printf("Expanding resource: ref=%s\n", String8(ref).string()));
1800 NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
1801 String8(package).string(), String8(type).string(),
1802 String8(name).string()));
1803 return 0;
1804 }
1805 uint32_t res = getResId(package, type, name, onlyPublic);
1806 NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
1807 String8(package).string(), String8(type).string(),
1808 String8(name).string(), res));
1809 if (res == 0) {
1810 if (outErrorMsg)
1811 *outErrorMsg = "No resource found that matches the given name";
1812 }
1813 return res;
1814}
1815
1816bool ResourceTable::isValidResourceName(const String16& s)
1817{
1818 const char16_t* p = s.string();
1819 bool first = true;
1820 while (*p) {
1821 if ((*p >= 'a' && *p <= 'z')
1822 || (*p >= 'A' && *p <= 'Z')
1823 || *p == '_'
1824 || (!first && *p >= '0' && *p <= '9')) {
1825 first = false;
1826 p++;
1827 continue;
1828 }
1829 return false;
1830 }
1831 return true;
1832}
1833
1834bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
1835 const String16& str,
1836 bool preserveSpaces, bool coerceType,
1837 uint32_t attrID,
1838 const Vector<StringPool::entry_style_span>* style,
1839 String16* outStr, void* accessorCookie,
1840 uint32_t attrType)
1841{
1842 String16 finalStr;
1843
1844 bool res = true;
1845 if (style == NULL || style->size() == 0) {
1846 // Text is not styled so it can be any type... let's figure it out.
1847 res = mAssets->getIncludedResources()
1848 .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
1849 coerceType, attrID, NULL, &mAssetsPackage, this,
1850 accessorCookie, attrType);
1851 } else {
1852 // Styled text can only be a string, and while collecting the style
1853 // information we have already processed that string!
1854 outValue->size = sizeof(Res_value);
1855 outValue->res0 = 0;
1856 outValue->dataType = outValue->TYPE_STRING;
1857 outValue->data = 0;
1858 finalStr = str;
1859 }
1860
1861 if (!res) {
1862 return false;
1863 }
1864
1865 if (outValue->dataType == outValue->TYPE_STRING) {
1866 // Should do better merging styles.
1867 if (pool) {
1868 if (style != NULL && style->size() > 0) {
1869 outValue->data = pool->add(finalStr, *style);
1870 } else {
1871 outValue->data = pool->add(finalStr, true);
1872 }
1873 } else {
1874 // Caller will fill this in later.
1875 outValue->data = 0;
1876 }
1877
1878 if (outStr) {
1879 *outStr = finalStr;
1880 }
1881
1882 }
1883
1884 return true;
1885}
1886
1887uint32_t ResourceTable::getCustomResource(
1888 const String16& package, const String16& type, const String16& name) const
1889{
1890 //printf("getCustomResource: %s %s %s\n", String8(package).string(),
1891 // String8(type).string(), String8(name).string());
1892 sp<Package> p = mPackages.valueFor(package);
1893 if (p == NULL) return 0;
1894 sp<Type> t = p->getTypes().valueFor(type);
1895 if (t == NULL) return 0;
1896 sp<ConfigList> c = t->getConfigs().valueFor(name);
1897 if (c == NULL) return 0;
1898 int32_t ei = c->getEntryIndex();
1899 if (ei < 0) return 0;
1900 return getResId(p, t, ei);
1901}
1902
1903uint32_t ResourceTable::getCustomResourceWithCreation(
1904 const String16& package, const String16& type, const String16& name,
1905 const bool createIfNotFound)
1906{
1907 uint32_t resId = getCustomResource(package, type, name);
1908 if (resId != 0 || !createIfNotFound) {
1909 return resId;
1910 }
1911 String16 value("false");
1912
1913 status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
1914 if (status == NO_ERROR) {
1915 resId = getResId(package, type, name);
1916 return resId;
1917 }
1918 return 0;
1919}
1920
1921uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
1922{
1923 return origPackage;
1924}
1925
1926bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
1927{
1928 //printf("getAttributeType #%08x\n", attrID);
1929 Res_value value;
1930 if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
1931 //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
1932 // String8(getEntry(attrID)->getName()).string(), value.data);
1933 *outType = value.data;
1934 return true;
1935 }
1936 return false;
1937}
1938
1939bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
1940{
1941 //printf("getAttributeMin #%08x\n", attrID);
1942 Res_value value;
1943 if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
1944 *outMin = value.data;
1945 return true;
1946 }
1947 return false;
1948}
1949
1950bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
1951{
1952 //printf("getAttributeMax #%08x\n", attrID);
1953 Res_value value;
1954 if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
1955 *outMax = value.data;
1956 return true;
1957 }
1958 return false;
1959}
1960
1961uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
1962{
1963 //printf("getAttributeL10N #%08x\n", attrID);
1964 Res_value value;
1965 if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
1966 return value.data;
1967 }
1968 return ResTable_map::L10N_NOT_REQUIRED;
1969}
1970
1971bool ResourceTable::getLocalizationSetting()
1972{
1973 return mBundle->getRequireLocalization();
1974}
1975
1976void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
1977{
1978 if (accessorCookie != NULL && fmt != NULL) {
1979 AccessorCookie* ac = (AccessorCookie*)accessorCookie;
1980 int retval=0;
1981 char buf[1024];
1982 va_list ap;
1983 va_start(ap, fmt);
1984 retval = vsnprintf(buf, sizeof(buf), fmt, ap);
1985 va_end(ap);
1986 ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
1987 buf, ac->attr.string(), ac->value.string());
1988 }
1989}
1990
1991bool ResourceTable::getAttributeKeys(
1992 uint32_t attrID, Vector<String16>* outKeys)
1993{
1994 sp<const Entry> e = getEntry(attrID);
1995 if (e != NULL) {
1996 const size_t N = e->getBag().size();
1997 for (size_t i=0; i<N; i++) {
1998 const String16& key = e->getBag().keyAt(i);
1999 if (key.size() > 0 && key.string()[0] != '^') {
2000 outKeys->add(key);
2001 }
2002 }
2003 return true;
2004 }
2005 return false;
2006}
2007
2008bool ResourceTable::getAttributeEnum(
2009 uint32_t attrID, const char16_t* name, size_t nameLen,
2010 Res_value* outValue)
2011{
2012 //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
2013 String16 nameStr(name, nameLen);
2014 sp<const Entry> e = getEntry(attrID);
2015 if (e != NULL) {
2016 const size_t N = e->getBag().size();
2017 for (size_t i=0; i<N; i++) {
2018 //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
2019 // String8(e->getBag().keyAt(i)).string());
2020 if (e->getBag().keyAt(i) == nameStr) {
2021 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
2022 }
2023 }
2024 }
2025 return false;
2026}
2027
2028bool ResourceTable::getAttributeFlags(
2029 uint32_t attrID, const char16_t* name, size_t nameLen,
2030 Res_value* outValue)
2031{
2032 outValue->dataType = Res_value::TYPE_INT_HEX;
2033 outValue->data = 0;
2034
2035 //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
2036 String16 nameStr(name, nameLen);
2037 sp<const Entry> e = getEntry(attrID);
2038 if (e != NULL) {
2039 const size_t N = e->getBag().size();
2040
2041 const char16_t* end = name + nameLen;
2042 const char16_t* pos = name;
2043 bool failed = false;
2044 while (pos < end && !failed) {
2045 const char16_t* start = pos;
2046 end++;
2047 while (pos < end && *pos != '|') {
2048 pos++;
2049 }
2050
2051 String16 nameStr(start, pos-start);
2052 size_t i;
2053 for (i=0; i<N; i++) {
2054 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
2055 // String8(e->getBag().keyAt(i)).string());
2056 if (e->getBag().keyAt(i) == nameStr) {
2057 Res_value val;
2058 bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
2059 if (!got) {
2060 return false;
2061 }
2062 //printf("Got value: 0x%08x\n", val.data);
2063 outValue->data |= val.data;
2064 break;
2065 }
2066 }
2067
2068 if (i >= N) {
2069 // Didn't find this flag identifier.
2070 return false;
2071 }
2072 if (pos < end) {
2073 pos++;
2074 }
2075 }
2076
2077 return true;
2078 }
2079 return false;
2080}
2081
2082status_t ResourceTable::assignResourceIds()
2083{
2084 const size_t N = mOrderedPackages.size();
2085 size_t pi;
2086 status_t firstError = NO_ERROR;
2087
2088 // First generate all bag attributes and assign indices.
2089 for (pi=0; pi<N; pi++) {
2090 sp<Package> p = mOrderedPackages.itemAt(pi);
2091 if (p == NULL || p->getTypes().size() == 0) {
2092 // Empty, skip!
2093 continue;
2094 }
2095
2096 status_t err = p->applyPublicTypeOrder();
2097 if (err != NO_ERROR && firstError == NO_ERROR) {
2098 firstError = err;
2099 }
2100
2101 // Generate attributes...
2102 const size_t N = p->getOrderedTypes().size();
2103 size_t ti;
2104 for (ti=0; ti<N; ti++) {
2105 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2106 if (t == NULL) {
2107 continue;
2108 }
2109 const size_t N = t->getOrderedConfigs().size();
2110 for (size_t ci=0; ci<N; ci++) {
2111 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2112 if (c == NULL) {
2113 continue;
2114 }
2115 const size_t N = c->getEntries().size();
2116 for (size_t ei=0; ei<N; ei++) {
2117 sp<Entry> e = c->getEntries().valueAt(ei);
2118 if (e == NULL) {
2119 continue;
2120 }
2121 status_t err = e->generateAttributes(this, p->getName());
2122 if (err != NO_ERROR && firstError == NO_ERROR) {
2123 firstError = err;
2124 }
2125 }
2126 }
2127 }
2128
2129 const SourcePos unknown(String8("????"), 0);
2130 sp<Type> attr = p->getType(String16("attr"), unknown);
2131
2132 // Assign indices...
2133 for (ti=0; ti<N; ti++) {
2134 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2135 if (t == NULL) {
2136 continue;
2137 }
2138 err = t->applyPublicEntryOrder();
2139 if (err != NO_ERROR && firstError == NO_ERROR) {
2140 firstError = err;
2141 }
2142
2143 const size_t N = t->getOrderedConfigs().size();
2144 t->setIndex(ti+1);
2145
2146 LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
2147 "First type is not attr!");
2148
2149 for (size_t ei=0; ei<N; ei++) {
2150 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
2151 if (c == NULL) {
2152 continue;
2153 }
2154 c->setEntryIndex(ei);
2155 }
2156 }
2157
2158 // Assign resource IDs to keys in bags...
2159 for (ti=0; ti<N; ti++) {
2160 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2161 if (t == NULL) {
2162 continue;
2163 }
2164 const size_t N = t->getOrderedConfigs().size();
2165 for (size_t ci=0; ci<N; ci++) {
2166 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2167 //printf("Ordered config #%d: %p\n", ci, c.get());
2168 const size_t N = c->getEntries().size();
2169 for (size_t ei=0; ei<N; ei++) {
2170 sp<Entry> e = c->getEntries().valueAt(ei);
2171 if (e == NULL) {
2172 continue;
2173 }
2174 status_t err = e->assignResourceIds(this, p->getName());
2175 if (err != NO_ERROR && firstError == NO_ERROR) {
2176 firstError = err;
2177 }
2178 }
2179 }
2180 }
2181 }
2182 return firstError;
2183}
2184
2185status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
2186 const size_t N = mOrderedPackages.size();
2187 size_t pi;
2188
2189 for (pi=0; pi<N; pi++) {
2190 sp<Package> p = mOrderedPackages.itemAt(pi);
2191 if (p->getTypes().size() == 0) {
2192 // Empty, skip!
2193 continue;
2194 }
2195
2196 const size_t N = p->getOrderedTypes().size();
2197 size_t ti;
2198
2199 for (ti=0; ti<N; ti++) {
2200 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2201 if (t == NULL) {
2202 continue;
2203 }
2204 const size_t N = t->getOrderedConfigs().size();
2205 sp<AaptSymbols> typeSymbols;
2206 typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
2207 for (size_t ci=0; ci<N; ci++) {
2208 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2209 if (c == NULL) {
2210 continue;
2211 }
2212 uint32_t rid = getResId(p, t, ci);
2213 if (rid == 0) {
2214 return UNKNOWN_ERROR;
2215 }
2216 if (Res_GETPACKAGE(rid) == (size_t)(p->getAssignedId()-1)) {
2217 typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
2218
2219 String16 comment(c->getComment());
2220 typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
2221 //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(),
2222 // String8(comment).string());
2223 comment = c->getTypeComment();
2224 typeSymbols->appendTypeComment(String8(c->getName()), comment);
2225 } else {
2226#if 0
2227 printf("**** NO MATCH: 0x%08x vs 0x%08x\n",
2228 Res_GETPACKAGE(rid), p->getAssignedId());
2229#endif
2230 }
2231 }
2232 }
2233 }
2234 return NO_ERROR;
2235}
2236
2237
2238void
2239ResourceTable::addLocalization(const String16& name, const String8& locale)
2240{
2241 mLocalizations[name].insert(locale);
2242}
2243
2244
2245/*!
2246 * Flag various sorts of localization problems. '+' indicates checks already implemented;
2247 * '-' indicates checks that will be implemented in the future.
2248 *
2249 * + A localized string for which no default-locale version exists => warning
2250 * + A string for which no version in an explicitly-requested locale exists => warning
2251 * + A localized translation of an translateable="false" string => warning
2252 * - A localized string not provided in every locale used by the table
2253 */
2254status_t
2255ResourceTable::validateLocalizations(void)
2256{
2257 status_t err = NO_ERROR;
2258 const String8 defaultLocale;
2259
2260 // For all strings...
2261 for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
2262 nameIter != mLocalizations.end();
2263 nameIter++) {
2264 const set<String8>& configSet = nameIter->second; // naming convenience
2265
2266 // Look for strings with no default localization
2267 if (configSet.count(defaultLocale) == 0) {
2268 fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
2269 String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
2270 for (set<String8>::iterator locales = configSet.begin();
2271 locales != configSet.end();
2272 locales++) {
2273 fprintf(stdout, " %s", (*locales).string());
2274 }
2275 fprintf(stdout, "\n");
2276 // !!! TODO: throw an error here in some circumstances
2277 }
2278
2279 // Check that all requested localizations are present for this string
2280 if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
2281 const char* allConfigs = mBundle->getConfigurations();
2282 const char* start = allConfigs;
2283 const char* comma;
2284
2285 do {
2286 String8 config;
2287 comma = strchr(start, ',');
2288 if (comma != NULL) {
2289 config.setTo(start, comma - start);
2290 start = comma + 1;
2291 } else {
2292 config.setTo(start);
2293 }
2294
2295 // don't bother with the pseudolocale "zz_ZZ"
2296 if (config != "zz_ZZ") {
2297 if (configSet.find(config) == configSet.end()) {
2298 // okay, no specific localization found. it's possible that we are
2299 // requiring a specific regional localization [e.g. de_DE] but there is an
2300 // available string in the generic language localization [e.g. de];
2301 // consider that string to have fulfilled the localization requirement.
2302 String8 region(config.string(), 2);
2303 if (configSet.find(region) == configSet.end()) {
2304 if (configSet.count(defaultLocale) == 0) {
2305 fprintf(stdout, "aapt: error: "
2306 "*** string '%s' has no default or required localization "
2307 "for '%s' in %s\n",
2308 String8(nameIter->first).string(),
2309 config.string(),
2310 mBundle->getResourceSourceDirs()[0]);
2311 err = UNKNOWN_ERROR;
2312 }
2313 }
2314 }
2315 }
2316 } while (comma != NULL);
2317 }
2318 }
2319
2320 return err;
2321}
2322
2323
2324status_t
2325ResourceFilter::parse(const char* arg)
2326{
2327 if (arg == NULL) {
2328 return 0;
2329 }
2330
2331 const char* p = arg;
2332 const char* q;
2333
2334 while (true) {
2335 q = strchr(p, ',');
2336 if (q == NULL) {
2337 q = p + strlen(p);
2338 }
2339
2340 String8 part(p, q-p);
2341
2342 if (part == "zz_ZZ") {
2343 mContainsPseudo = true;
2344 }
2345 int axis;
2346 uint32_t value;
2347 if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
2348 fprintf(stderr, "Invalid configuration: %s\n", arg);
2349 fprintf(stderr, " ");
2350 for (int i=0; i<p-arg; i++) {
2351 fprintf(stderr, " ");
2352 }
2353 for (int i=0; i<q-p; i++) {
2354 fprintf(stderr, "^");
2355 }
2356 fprintf(stderr, "\n");
2357 return 1;
2358 }
2359
2360 ssize_t index = mData.indexOfKey(axis);
2361 if (index < 0) {
2362 mData.add(axis, SortedVector<uint32_t>());
2363 }
2364 SortedVector<uint32_t>& sv = mData.editValueFor(axis);
2365 sv.add(value);
2366 // if it's a locale with a region, also match an unmodified locale of the
2367 // same language
2368 if (axis == AXIS_LANGUAGE) {
2369 if (value & 0xffff0000) {
2370 sv.add(value & 0x0000ffff);
2371 }
2372 }
2373 p = q;
2374 if (!*p) break;
2375 p++;
2376 }
2377
2378 return NO_ERROR;
2379}
2380
2381bool
2382ResourceFilter::match(int axis, uint32_t value)
2383{
2384 if (value == 0) {
2385 // they didn't specify anything so take everything
2386 return true;
2387 }
2388 ssize_t index = mData.indexOfKey(axis);
2389 if (index < 0) {
2390 // we didn't request anything on this axis so take everything
2391 return true;
2392 }
2393 const SortedVector<uint32_t>& sv = mData.valueAt(index);
2394 return sv.indexOf(value) >= 0;
2395}
2396
2397bool
2398ResourceFilter::match(const ResTable_config& config)
2399{
2400 if (config.locale) {
2401 uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
2402 | (config.language[1] << 8) | (config.language[0]);
2403 if (!match(AXIS_LANGUAGE, locale)) {
2404 return false;
2405 }
2406 }
2407 if (!match(AXIS_ORIENTATION, config.orientation)) {
2408 return false;
2409 }
2410 if (!match(AXIS_DENSITY, config.density)) {
2411 return false;
2412 }
2413 if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
2414 return false;
2415 }
2416 if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
2417 return false;
2418 }
2419 if (!match(AXIS_KEYBOARD, config.keyboard)) {
2420 return false;
2421 }
2422 if (!match(AXIS_NAVIGATION, config.navigation)) {
2423 return false;
2424 }
2425 if (!match(AXIS_SCREENSIZE, config.screenSize)) {
2426 return false;
2427 }
2428 if (!match(AXIS_VERSION, config.version)) {
2429 return false;
2430 }
2431 return true;
2432}
2433
2434status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
2435{
2436 ResourceFilter filter;
2437 status_t err = filter.parse(bundle->getConfigurations());
2438 if (err != NO_ERROR) {
2439 return err;
2440 }
2441
2442 const size_t N = mOrderedPackages.size();
2443 size_t pi;
2444
2445 // Iterate through all data, collecting all values (strings,
2446 // references, etc).
2447 StringPool valueStrings;
2448 for (pi=0; pi<N; pi++) {
2449 sp<Package> p = mOrderedPackages.itemAt(pi);
2450 if (p->getTypes().size() == 0) {
2451 // Empty, skip!
2452 continue;
2453 }
2454
2455 StringPool typeStrings;
2456 StringPool keyStrings;
2457
2458 const size_t N = p->getOrderedTypes().size();
2459 for (size_t ti=0; ti<N; ti++) {
2460 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2461 if (t == NULL) {
2462 typeStrings.add(String16("<empty>"), false);
2463 continue;
2464 }
2465 typeStrings.add(t->getName(), false);
2466
2467 const size_t N = t->getOrderedConfigs().size();
2468 for (size_t ci=0; ci<N; ci++) {
2469 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2470 if (c == NULL) {
2471 continue;
2472 }
2473 const size_t N = c->getEntries().size();
2474 for (size_t ei=0; ei<N; ei++) {
2475 ConfigDescription config = c->getEntries().keyAt(ei);
2476 if (!filter.match(config)) {
2477 continue;
2478 }
2479 sp<Entry> e = c->getEntries().valueAt(ei);
2480 if (e == NULL) {
2481 continue;
2482 }
2483 e->setNameIndex(keyStrings.add(e->getName(), true));
2484 status_t err = e->prepareFlatten(&valueStrings, this);
2485 if (err != NO_ERROR) {
2486 return err;
2487 }
2488 }
2489 }
2490 }
2491
2492 p->setTypeStrings(typeStrings.createStringBlock());
2493 p->setKeyStrings(keyStrings.createStringBlock());
2494 }
2495
2496 ssize_t strAmt = 0;
2497
2498 // Now build the array of package chunks.
2499 Vector<sp<AaptFile> > flatPackages;
2500 for (pi=0; pi<N; pi++) {
2501 sp<Package> p = mOrderedPackages.itemAt(pi);
2502 if (p->getTypes().size() == 0) {
2503 // Empty, skip!
2504 continue;
2505 }
2506
2507 const size_t N = p->getTypeStrings().size();
2508
2509 const size_t baseSize = sizeof(ResTable_package);
2510
2511 // Start the package data.
2512 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
2513 ResTable_package* header = (ResTable_package*)data->editData(baseSize);
2514 if (header == NULL) {
2515 fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
2516 return NO_MEMORY;
2517 }
2518 memset(header, 0, sizeof(*header));
2519 header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
2520 header->header.headerSize = htods(sizeof(*header));
2521 header->id = htodl(p->getAssignedId());
2522 strcpy16_htod(header->name, p->getName().string());
2523
2524 // Write the string blocks.
2525 const size_t typeStringsStart = data->getSize();
2526 sp<AaptFile> strFile = p->getTypeStringsData();
2527 ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
2528 #if PRINT_STRING_METRICS
2529 fprintf(stderr, "**** type strings: %d\n", amt);
2530 #endif
2531 strAmt += amt;
2532 if (amt < 0) {
2533 return amt;
2534 }
2535 const size_t keyStringsStart = data->getSize();
2536 strFile = p->getKeyStringsData();
2537 amt = data->writeData(strFile->getData(), strFile->getSize());
2538 #if PRINT_STRING_METRICS
2539 fprintf(stderr, "**** key strings: %d\n", amt);
2540 #endif
2541 strAmt += amt;
2542 if (amt < 0) {
2543 return amt;
2544 }
2545
2546 // Build the type chunks inside of this package.
2547 for (size_t ti=0; ti<N; ti++) {
2548 // Retrieve them in the same order as the type string block.
2549 size_t len;
2550 String16 typeName(p->getTypeStrings().stringAt(ti, &len));
2551 sp<Type> t = p->getTypes().valueFor(typeName);
2552 LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
2553 "Type name %s not found",
2554 String8(typeName).string());
2555
2556 const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
2557
2558 // First write the typeSpec chunk, containing information about
2559 // each resource entry in this type.
2560 {
2561 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
2562 const size_t typeSpecStart = data->getSize();
2563 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
2564 (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
2565 if (tsHeader == NULL) {
2566 fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
2567 return NO_MEMORY;
2568 }
2569 memset(tsHeader, 0, sizeof(*tsHeader));
2570 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
2571 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
2572 tsHeader->header.size = htodl(typeSpecSize);
2573 tsHeader->id = ti+1;
2574 tsHeader->entryCount = htodl(N);
2575
2576 uint32_t* typeSpecFlags = (uint32_t*)
2577 (((uint8_t*)data->editData())
2578 + typeSpecStart + sizeof(ResTable_typeSpec));
2579 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
2580
2581 for (size_t ei=0; ei<N; ei++) {
2582 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2583 if (cl->getPublic()) {
2584 typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
2585 }
2586 const size_t CN = cl->getEntries().size();
2587 for (size_t ci=0; ci<CN; ci++) {
2588 if (!filter.match(cl->getEntries().keyAt(ci))) {
2589 continue;
2590 }
2591 for (size_t cj=ci+1; cj<CN; cj++) {
2592 if (!filter.match(cl->getEntries().keyAt(cj))) {
2593 continue;
2594 }
2595 typeSpecFlags[ei] |= htodl(
2596 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
2597 }
2598 }
2599 }
2600 }
2601
2602 // We need to write one type chunk for each configuration for
2603 // which we have entries in this type.
2604 const size_t NC = t->getUniqueConfigs().size();
2605
2606 const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
2607
2608 for (size_t ci=0; ci<NC; ci++) {
2609 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
2610
2611 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
2612 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
2613 ti+1,
2614 config.mcc, config.mnc,
2615 config.language[0] ? config.language[0] : '-',
2616 config.language[1] ? config.language[1] : '-',
2617 config.country[0] ? config.country[0] : '-',
2618 config.country[1] ? config.country[1] : '-',
2619 config.orientation,
2620 config.touchscreen,
2621 config.density,
2622 config.keyboard,
2623 config.inputFlags,
2624 config.navigation,
2625 config.screenWidth,
2626 config.screenHeight));
2627
2628 if (!filter.match(config)) {
2629 continue;
2630 }
2631
2632 const size_t typeStart = data->getSize();
2633
2634 ResTable_type* tHeader = (ResTable_type*)
2635 (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
2636 if (tHeader == NULL) {
2637 fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
2638 return NO_MEMORY;
2639 }
2640
2641 memset(tHeader, 0, sizeof(*tHeader));
2642 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
2643 tHeader->header.headerSize = htods(sizeof(*tHeader));
2644 tHeader->id = ti+1;
2645 tHeader->entryCount = htodl(N);
2646 tHeader->entriesStart = htodl(typeSize);
2647 tHeader->config = config;
2648 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
2649 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
2650 ti+1,
2651 tHeader->config.mcc, tHeader->config.mnc,
2652 tHeader->config.language[0] ? tHeader->config.language[0] : '-',
2653 tHeader->config.language[1] ? tHeader->config.language[1] : '-',
2654 tHeader->config.country[0] ? tHeader->config.country[0] : '-',
2655 tHeader->config.country[1] ? tHeader->config.country[1] : '-',
2656 tHeader->config.orientation,
2657 tHeader->config.touchscreen,
2658 tHeader->config.density,
2659 tHeader->config.keyboard,
2660 tHeader->config.inputFlags,
2661 tHeader->config.navigation,
2662 tHeader->config.screenWidth,
2663 tHeader->config.screenHeight));
2664 tHeader->config.swapHtoD();
2665
2666 // Build the entries inside of this type.
2667 for (size_t ei=0; ei<N; ei++) {
2668 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2669 sp<Entry> e = cl->getEntries().valueFor(config);
2670
2671 // Set the offset for this entry in its type.
2672 uint32_t* index = (uint32_t*)
2673 (((uint8_t*)data->editData())
2674 + typeStart + sizeof(ResTable_type));
2675 if (e != NULL) {
2676 index[ei] = htodl(data->getSize()-typeStart-typeSize);
2677
2678 // Create the entry.
2679 ssize_t amt = e->flatten(bundle, data, cl->getPublic());
2680 if (amt < 0) {
2681 return amt;
2682 }
2683 } else {
2684 index[ei] = htodl(ResTable_type::NO_ENTRY);
2685 }
2686 }
2687
2688 // Fill in the rest of the type information.
2689 tHeader = (ResTable_type*)
2690 (((uint8_t*)data->editData()) + typeStart);
2691 tHeader->header.size = htodl(data->getSize()-typeStart);
2692 }
2693 }
2694
2695 // Fill in the rest of the package information.
2696 header = (ResTable_package*)data->editData();
2697 header->header.size = htodl(data->getSize());
2698 header->typeStrings = htodl(typeStringsStart);
2699 header->lastPublicType = htodl(p->getTypeStrings().size());
2700 header->keyStrings = htodl(keyStringsStart);
2701 header->lastPublicKey = htodl(p->getKeyStrings().size());
2702
2703 flatPackages.add(data);
2704 }
2705
2706 // And now write out the final chunks.
2707 const size_t dataStart = dest->getSize();
2708
2709 {
2710 // blah
2711 ResTable_header header;
2712 memset(&header, 0, sizeof(header));
2713 header.header.type = htods(RES_TABLE_TYPE);
2714 header.header.headerSize = htods(sizeof(header));
2715 header.packageCount = htodl(flatPackages.size());
2716 status_t err = dest->writeData(&header, sizeof(header));
2717 if (err != NO_ERROR) {
2718 fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
2719 return err;
2720 }
2721 }
2722
2723 ssize_t strStart = dest->getSize();
2724 err = valueStrings.writeStringBlock(dest);
2725 if (err != NO_ERROR) {
2726 return err;
2727 }
2728
2729 ssize_t amt = (dest->getSize()-strStart);
2730 strAmt += amt;
2731 #if PRINT_STRING_METRICS
2732 fprintf(stderr, "**** value strings: %d\n", amt);
2733 fprintf(stderr, "**** total strings: %d\n", strAmt);
2734 #endif
2735
2736 for (pi=0; pi<flatPackages.size(); pi++) {
2737 err = dest->writeData(flatPackages[pi]->getData(),
2738 flatPackages[pi]->getSize());
2739 if (err != NO_ERROR) {
2740 fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
2741 return err;
2742 }
2743 }
2744
2745 ResTable_header* header = (ResTable_header*)
2746 (((uint8_t*)dest->getData()) + dataStart);
2747 header->header.size = htodl(dest->getSize() - dataStart);
2748
2749 NOISY(aout << "Resource table:"
2750 << HexDump(dest->getData(), dest->getSize()) << endl);
2751
2752 #if PRINT_STRING_METRICS
2753 fprintf(stderr, "**** total resource table size: %d / %d%% strings\n",
2754 dest->getSize(), (strAmt*100)/dest->getSize());
2755 #endif
2756
2757 return NO_ERROR;
2758}
2759
2760void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
2761{
2762 fprintf(fp,
2763 "<!-- This file contains <public> resource definitions for all\n"
2764 " resources that were generated from the source data. -->\n"
2765 "\n"
2766 "<resources>\n");
2767
2768 writePublicDefinitions(package, fp, true);
2769 writePublicDefinitions(package, fp, false);
2770
2771 fprintf(fp,
2772 "\n"
2773 "</resources>\n");
2774}
2775
2776void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
2777{
2778 bool didHeader = false;
2779
2780 sp<Package> pkg = mPackages.valueFor(package);
2781 if (pkg != NULL) {
2782 const size_t NT = pkg->getOrderedTypes().size();
2783 for (size_t i=0; i<NT; i++) {
2784 sp<Type> t = pkg->getOrderedTypes().itemAt(i);
2785 if (t == NULL) {
2786 continue;
2787 }
2788
2789 bool didType = false;
2790
2791 const size_t NC = t->getOrderedConfigs().size();
2792 for (size_t j=0; j<NC; j++) {
2793 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
2794 if (c == NULL) {
2795 continue;
2796 }
2797
2798 if (c->getPublic() != pub) {
2799 continue;
2800 }
2801
2802 if (!didType) {
2803 fprintf(fp, "\n");
2804 didType = true;
2805 }
2806 if (!didHeader) {
2807 if (pub) {
2808 fprintf(fp," <!-- PUBLIC SECTION. These resources have been declared public.\n");
2809 fprintf(fp," Changes to these definitions will break binary compatibility. -->\n\n");
2810 } else {
2811 fprintf(fp," <!-- PRIVATE SECTION. These resources have not been declared public.\n");
2812 fprintf(fp," You can make them public my moving these lines into a file in res/values. -->\n\n");
2813 }
2814 didHeader = true;
2815 }
2816 if (!pub) {
2817 const size_t NE = c->getEntries().size();
2818 for (size_t k=0; k<NE; k++) {
2819 const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
2820 if (pos.file != "") {
2821 fprintf(fp," <!-- Declared at %s:%d -->\n",
2822 pos.file.string(), pos.line);
2823 }
2824 }
2825 }
2826 fprintf(fp, " <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
2827 String8(t->getName()).string(),
2828 String8(c->getName()).string(),
2829 getResId(pkg, t, c->getEntryIndex()));
2830 }
2831 }
2832 }
2833}
2834
2835ResourceTable::Item::Item(const SourcePos& _sourcePos,
2836 bool _isId,
2837 const String16& _value,
2838 const Vector<StringPool::entry_style_span>* _style,
2839 int32_t _format)
2840 : sourcePos(_sourcePos)
2841 , isId(_isId)
2842 , value(_value)
2843 , format(_format)
2844 , bagKeyId(0)
2845 , evaluating(false)
2846{
2847 if (_style) {
2848 style = *_style;
2849 }
2850}
2851
2852status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
2853{
2854 if (mType == TYPE_BAG) {
2855 return NO_ERROR;
2856 }
2857 if (mType == TYPE_UNKNOWN) {
2858 mType = TYPE_BAG;
2859 return NO_ERROR;
2860 }
2861 sourcePos.error("Resource entry %s is already defined as a single item.\n"
2862 "%s:%d: Originally defined here.\n",
2863 String8(mName).string(),
2864 mItem.sourcePos.file.string(), mItem.sourcePos.line);
2865 return UNKNOWN_ERROR;
2866}
2867
2868status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
2869 const String16& value,
2870 const Vector<StringPool::entry_style_span>* style,
2871 int32_t format,
2872 const bool overwrite)
2873{
2874 Item item(sourcePos, false, value, style);
2875
2876 if (mType == TYPE_BAG) {
2877 const Item& item(mBag.valueAt(0));
2878 sourcePos.error("Resource entry %s is already defined as a bag.\n"
2879 "%s:%d: Originally defined here.\n",
2880 String8(mName).string(),
2881 item.sourcePos.file.string(), item.sourcePos.line);
2882 return UNKNOWN_ERROR;
2883 }
2884 if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
2885 sourcePos.error("Resource entry %s is already defined.\n"
2886 "%s:%d: Originally defined here.\n",
2887 String8(mName).string(),
2888 mItem.sourcePos.file.string(), mItem.sourcePos.line);
2889 return UNKNOWN_ERROR;
2890 }
2891
2892 mType = TYPE_ITEM;
2893 mItem = item;
2894 mItemFormat = format;
2895 return NO_ERROR;
2896}
2897
2898status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
2899 const String16& key, const String16& value,
2900 const Vector<StringPool::entry_style_span>* style,
2901 bool replace, bool isId, int32_t format)
2902{
2903 status_t err = makeItABag(sourcePos);
2904 if (err != NO_ERROR) {
2905 return err;
2906 }
2907
2908 Item item(sourcePos, isId, value, style, format);
2909
2910 // XXX NOTE: there is an error if you try to have a bag with two keys,
2911 // one an attr and one an id, with the same name. Not something we
2912 // currently ever have to worry about.
2913 ssize_t origKey = mBag.indexOfKey(key);
2914 if (origKey >= 0) {
2915 if (!replace) {
2916 const Item& item(mBag.valueAt(origKey));
2917 sourcePos.error("Resource entry %s already has bag item %s.\n"
2918 "%s:%d: Originally defined here.\n",
2919 String8(mName).string(), String8(key).string(),
2920 item.sourcePos.file.string(), item.sourcePos.line);
2921 return UNKNOWN_ERROR;
2922 }
2923 //printf("Replacing %s with %s\n",
2924 // String8(mBag.valueFor(key).value).string(), String8(value).string());
2925 mBag.replaceValueFor(key, item);
2926 }
2927
2928 mBag.add(key, item);
2929 return NO_ERROR;
2930}
2931
Robert Greenwalt4b4f4a92009-04-02 16:55:50 -07002932status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
2933{
2934 status_t err = makeItABag(sourcePos);
2935 if (err != NO_ERROR) {
2936 return err;
2937 }
2938
2939 mBag.clear();
2940 return NO_ERROR;
2941}
2942
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002943status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
2944 const String16& package)
2945{
2946 const String16 attr16("attr");
2947 const String16 id16("id");
2948 const size_t N = mBag.size();
2949 for (size_t i=0; i<N; i++) {
2950 const String16& key = mBag.keyAt(i);
2951 const Item& it = mBag.valueAt(i);
2952 if (it.isId) {
2953 if (!table->hasBagOrEntry(key, &id16, &package)) {
2954 String16 value("false");
2955 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
2956 id16, key, value);
2957 if (err != NO_ERROR) {
2958 return err;
2959 }
2960 }
2961 } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
2962
2963#if 1
2964// fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
2965// String8(key).string());
2966// const Item& item(mBag.valueAt(i));
2967// fprintf(stderr, "Referenced from file %s line %d\n",
2968// item.sourcePos.file.string(), item.sourcePos.line);
2969// return UNKNOWN_ERROR;
2970#else
2971 char numberStr[16];
2972 sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
2973 status_t err = table->addBag(SourcePos("<generated>", 0), package,
2974 attr16, key, String16(""),
2975 String16("^type"),
2976 String16(numberStr), NULL, NULL);
2977 if (err != NO_ERROR) {
2978 return err;
2979 }
2980#endif
2981 }
2982 }
2983 return NO_ERROR;
2984}
2985
2986status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
2987 const String16& package)
2988{
2989 bool hasErrors = false;
2990
2991 if (mType == TYPE_BAG) {
2992 const char* errorMsg;
2993 const String16 style16("style");
2994 const String16 attr16("attr");
2995 const String16 id16("id");
2996 mParentId = 0;
2997 if (mParent.size() > 0) {
2998 mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
2999 if (mParentId == 0) {
3000 mPos.error("Error retrieving parent for item: %s '%s'.\n",
3001 errorMsg, String8(mParent).string());
3002 hasErrors = true;
3003 }
3004 }
3005 const size_t N = mBag.size();
3006 for (size_t i=0; i<N; i++) {
3007 const String16& key = mBag.keyAt(i);
3008 Item& it = mBag.editValueAt(i);
3009 it.bagKeyId = table->getResId(key,
3010 it.isId ? &id16 : &attr16, NULL, &errorMsg);
3011 //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
3012 if (it.bagKeyId == 0) {
3013 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
3014 String8(it.isId ? id16 : attr16).string(),
3015 String8(key).string());
3016 hasErrors = true;
3017 }
3018 }
3019 }
3020 return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
3021}
3022
3023status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
3024{
3025 if (mType == TYPE_ITEM) {
3026 Item& it = mItem;
3027 AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
3028 if (!table->stringToValue(&it.parsedValue, strings,
3029 it.value, false, true, 0,
3030 &it.style, NULL, &ac, mItemFormat)) {
3031 return UNKNOWN_ERROR;
3032 }
3033 } else if (mType == TYPE_BAG) {
3034 const size_t N = mBag.size();
3035 for (size_t i=0; i<N; i++) {
3036 const String16& key = mBag.keyAt(i);
3037 Item& it = mBag.editValueAt(i);
3038 AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
3039 if (!table->stringToValue(&it.parsedValue, strings,
3040 it.value, false, true, it.bagKeyId,
3041 &it.style, NULL, &ac, it.format)) {
3042 return UNKNOWN_ERROR;
3043 }
3044 }
3045 } else {
3046 mPos.error("Error: entry %s is not a single item or a bag.\n",
3047 String8(mName).string());
3048 return UNKNOWN_ERROR;
3049 }
3050 return NO_ERROR;
3051}
3052
3053ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
3054{
3055 size_t amt = 0;
3056 ResTable_entry header;
3057 memset(&header, 0, sizeof(header));
3058 header.size = htods(sizeof(header));
3059 const type ty = this != NULL ? mType : TYPE_ITEM;
3060 if (this != NULL) {
3061 if (ty == TYPE_BAG) {
3062 header.flags |= htods(header.FLAG_COMPLEX);
3063 }
3064 if (isPublic) {
3065 header.flags |= htods(header.FLAG_PUBLIC);
3066 }
3067 header.key.index = htodl(mNameIndex);
3068 }
3069 if (ty != TYPE_BAG) {
3070 status_t err = data->writeData(&header, sizeof(header));
3071 if (err != NO_ERROR) {
3072 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3073 return err;
3074 }
3075
3076 const Item& it = mItem;
3077 Res_value par;
3078 memset(&par, 0, sizeof(par));
3079 par.size = htods(it.parsedValue.size);
3080 par.dataType = it.parsedValue.dataType;
3081 par.res0 = it.parsedValue.res0;
3082 par.data = htodl(it.parsedValue.data);
3083 #if 0
3084 printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
3085 String8(mName).string(), it.parsedValue.dataType,
3086 it.parsedValue.data, par.res0);
3087 #endif
3088 err = data->writeData(&par, it.parsedValue.size);
3089 if (err != NO_ERROR) {
3090 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3091 return err;
3092 }
3093 amt += it.parsedValue.size;
3094 } else {
3095 size_t N = mBag.size();
3096 size_t i;
3097 // Create correct ordering of items.
3098 KeyedVector<uint32_t, const Item*> items;
3099 for (i=0; i<N; i++) {
3100 const Item& it = mBag.valueAt(i);
3101 items.add(it.bagKeyId, &it);
3102 }
3103 N = items.size();
3104
3105 ResTable_map_entry mapHeader;
3106 memcpy(&mapHeader, &header, sizeof(header));
3107 mapHeader.size = htods(sizeof(mapHeader));
3108 mapHeader.parent.ident = htodl(mParentId);
3109 mapHeader.count = htodl(N);
3110 status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
3111 if (err != NO_ERROR) {
3112 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3113 return err;
3114 }
3115
3116 for (i=0; i<N; i++) {
3117 const Item& it = *items.valueAt(i);
3118 ResTable_map map;
3119 map.name.ident = htodl(it.bagKeyId);
3120 map.value.size = htods(it.parsedValue.size);
3121 map.value.dataType = it.parsedValue.dataType;
3122 map.value.res0 = it.parsedValue.res0;
3123 map.value.data = htodl(it.parsedValue.data);
3124 err = data->writeData(&map, sizeof(map));
3125 if (err != NO_ERROR) {
3126 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3127 return err;
3128 }
3129 amt += sizeof(map);
3130 }
3131 }
3132 return amt;
3133}
3134
3135void ResourceTable::ConfigList::appendComment(const String16& comment,
3136 bool onlyIfEmpty)
3137{
3138 if (comment.size() <= 0) {
3139 return;
3140 }
3141 if (onlyIfEmpty && mComment.size() > 0) {
3142 return;
3143 }
3144 if (mComment.size() > 0) {
3145 mComment.append(String16("\n"));
3146 }
3147 mComment.append(comment);
3148}
3149
3150void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
3151{
3152 if (comment.size() <= 0) {
3153 return;
3154 }
3155 if (mTypeComment.size() > 0) {
3156 mTypeComment.append(String16("\n"));
3157 }
3158 mTypeComment.append(comment);
3159}
3160
3161status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
3162 const String16& name,
3163 const uint32_t ident)
3164{
3165 #if 0
3166 int32_t entryIdx = Res_GETENTRY(ident);
3167 if (entryIdx < 0) {
3168 sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
3169 String8(mName).string(), String8(name).string(), ident);
3170 return UNKNOWN_ERROR;
3171 }
3172 #endif
3173
3174 int32_t typeIdx = Res_GETTYPE(ident);
3175 if (typeIdx >= 0) {
3176 typeIdx++;
3177 if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
3178 sourcePos.error("Public resource %s/%s has conflicting type codes for its"
3179 " public identifiers (0x%x vs 0x%x).\n",
3180 String8(mName).string(), String8(name).string(),
3181 mPublicIndex, typeIdx);
3182 return UNKNOWN_ERROR;
3183 }
3184 mPublicIndex = typeIdx;
3185 }
3186
3187 if (mFirstPublicSourcePos == NULL) {
3188 mFirstPublicSourcePos = new SourcePos(sourcePos);
3189 }
3190
3191 if (mPublic.indexOfKey(name) < 0) {
3192 mPublic.add(name, Public(sourcePos, String16(), ident));
3193 } else {
3194 Public& p = mPublic.editValueFor(name);
3195 if (p.ident != ident) {
3196 sourcePos.error("Public resource %s/%s has conflicting public identifiers"
3197 " (0x%08x vs 0x%08x).\n"
3198 "%s:%d: Originally defined here.\n",
3199 String8(mName).string(), String8(name).string(), p.ident, ident,
3200 p.sourcePos.file.string(), p.sourcePos.line);
3201 return UNKNOWN_ERROR;
3202 }
3203 }
3204
3205 return NO_ERROR;
3206}
3207
3208sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
3209 const SourcePos& sourcePos,
3210 const ResTable_config* config,
3211 bool doSetIndex)
3212{
3213 int pos = -1;
3214 sp<ConfigList> c = mConfigs.valueFor(entry);
3215 if (c == NULL) {
3216 c = new ConfigList(entry, sourcePos);
3217 mConfigs.add(entry, c);
3218 pos = (int)mOrderedConfigs.size();
3219 mOrderedConfigs.add(c);
3220 if (doSetIndex) {
3221 c->setEntryIndex(pos);
3222 }
3223 }
3224
3225 ConfigDescription cdesc;
3226 if (config) cdesc = *config;
3227
3228 sp<Entry> e = c->getEntries().valueFor(cdesc);
3229 if (e == NULL) {
3230 if (config != NULL) {
3231 NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
3232 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
3233 sourcePos.file.string(), sourcePos.line,
3234 config->mcc, config->mnc,
3235 config->language[0] ? config->language[0] : '-',
3236 config->language[1] ? config->language[1] : '-',
3237 config->country[0] ? config->country[0] : '-',
3238 config->country[1] ? config->country[1] : '-',
3239 config->orientation,
3240 config->touchscreen,
3241 config->density,
3242 config->keyboard,
3243 config->inputFlags,
3244 config->navigation,
3245 config->screenWidth,
3246 config->screenHeight));
3247 } else {
3248 NOISY(printf("New entry at %s:%d: NULL config\n",
3249 sourcePos.file.string(), sourcePos.line));
3250 }
3251 e = new Entry(entry, sourcePos);
3252 c->addEntry(cdesc, e);
3253 /*
3254 if (doSetIndex) {
3255 if (pos < 0) {
3256 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
3257 if (mOrderedConfigs[pos] == c) {
3258 break;
3259 }
3260 }
3261 if (pos >= (int)mOrderedConfigs.size()) {
3262 sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
3263 return NULL;
3264 }
3265 }
3266 e->setEntryIndex(pos);
3267 }
3268 */
3269 }
3270
3271 mUniqueConfigs.add(cdesc);
3272
3273 return e;
3274}
3275
3276status_t ResourceTable::Type::applyPublicEntryOrder()
3277{
3278 size_t N = mOrderedConfigs.size();
3279 Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
3280 bool hasError = false;
3281
3282 size_t i;
3283 for (i=0; i<N; i++) {
3284 mOrderedConfigs.replaceAt(NULL, i);
3285 }
3286
3287 const size_t NP = mPublic.size();
3288 //printf("Ordering %d configs from %d public defs\n", N, NP);
3289 size_t j;
3290 for (j=0; j<NP; j++) {
3291 const String16& name = mPublic.keyAt(j);
3292 const Public& p = mPublic.valueAt(j);
3293 int32_t idx = Res_GETENTRY(p.ident);
3294 //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
3295 // String8(mName).string(), String8(name).string(), p.ident, N);
3296 bool found = false;
3297 for (i=0; i<N; i++) {
3298 sp<ConfigList> e = origOrder.itemAt(i);
3299 //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
3300 if (e->getName() == name) {
3301 if (idx >= (int32_t)mOrderedConfigs.size()) {
3302 p.sourcePos.error("Public entry identifier 0x%x entry index "
3303 "is larger than available symbols (index %d, total symbols %d).\n",
3304 p.ident, idx, mOrderedConfigs.size());
3305 hasError = true;
3306 } else if (mOrderedConfigs.itemAt(idx) == NULL) {
3307 e->setPublic(true);
3308 e->setPublicSourcePos(p.sourcePos);
3309 mOrderedConfigs.replaceAt(e, idx);
3310 origOrder.removeAt(i);
3311 N--;
3312 found = true;
3313 break;
3314 } else {
3315 sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
3316
3317 p.sourcePos.error("Multiple entry names declared for public entry"
3318 " identifier 0x%x in type %s (%s vs %s).\n"
3319 "%s:%d: Originally defined here.",
3320 idx+1, String8(mName).string(),
3321 String8(oe->getName()).string(),
3322 String8(name).string(),
3323 oe->getPublicSourcePos().file.string(),
3324 oe->getPublicSourcePos().line);
3325 hasError = true;
3326 }
3327 }
3328 }
3329
3330 if (!found) {
3331 p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
3332 String8(mName).string(), String8(name).string());
3333 hasError = true;
3334 }
3335 }
3336
3337 //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
3338
3339 if (N != origOrder.size()) {
3340 printf("Internal error: remaining private symbol count mismatch\n");
3341 N = origOrder.size();
3342 }
3343
3344 j = 0;
3345 for (i=0; i<N; i++) {
3346 sp<ConfigList> e = origOrder.itemAt(i);
3347 // There will always be enough room for the remaining entries.
3348 while (mOrderedConfigs.itemAt(j) != NULL) {
3349 j++;
3350 }
3351 mOrderedConfigs.replaceAt(e, j);
3352 j++;
3353 }
3354
3355 return hasError ? UNKNOWN_ERROR : NO_ERROR;
3356}
3357
3358ResourceTable::Package::Package(const String16& name, ssize_t includedId)
3359 : mName(name), mIncludedId(includedId),
3360 mTypeStringsMapping(0xffffffff),
3361 mKeyStringsMapping(0xffffffff)
3362{
3363}
3364
3365sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
3366 const SourcePos& sourcePos,
3367 bool doSetIndex)
3368{
3369 sp<Type> t = mTypes.valueFor(type);
3370 if (t == NULL) {
3371 t = new Type(type, sourcePos);
3372 mTypes.add(type, t);
3373 mOrderedTypes.add(t);
3374 if (doSetIndex) {
3375 // For some reason the type's index is set to one plus the index
3376 // in the mOrderedTypes list, rather than just the index.
3377 t->setIndex(mOrderedTypes.size());
3378 }
3379 }
3380 return t;
3381}
3382
3383status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
3384{
3385 mTypeStringsData = data;
3386 status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
3387 if (err != NO_ERROR) {
3388 fprintf(stderr, "ERROR: Type string data is corrupt!\n");
3389 }
3390 return err;
3391}
3392
3393status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
3394{
3395 mKeyStringsData = data;
3396 status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
3397 if (err != NO_ERROR) {
3398 fprintf(stderr, "ERROR: Key string data is corrupt!\n");
3399 }
3400 return err;
3401}
3402
3403status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
3404 ResStringPool* strings,
3405 DefaultKeyedVector<String16, uint32_t>* mappings)
3406{
3407 if (data->getData() == NULL) {
3408 return UNKNOWN_ERROR;
3409 }
3410
3411 NOISY(aout << "Setting restable string pool: "
3412 << HexDump(data->getData(), data->getSize()) << endl);
3413
3414 status_t err = strings->setTo(data->getData(), data->getSize());
3415 if (err == NO_ERROR) {
3416 const size_t N = strings->size();
3417 for (size_t i=0; i<N; i++) {
3418 size_t len;
3419 mappings->add(String16(strings->stringAt(i, &len)), i);
3420 }
3421 }
3422 return err;
3423}
3424
3425status_t ResourceTable::Package::applyPublicTypeOrder()
3426{
3427 size_t N = mOrderedTypes.size();
3428 Vector<sp<Type> > origOrder(mOrderedTypes);
3429
3430 size_t i;
3431 for (i=0; i<N; i++) {
3432 mOrderedTypes.replaceAt(NULL, i);
3433 }
3434
3435 for (i=0; i<N; i++) {
3436 sp<Type> t = origOrder.itemAt(i);
3437 int32_t idx = t->getPublicIndex();
3438 if (idx > 0) {
3439 idx--;
3440 while (idx >= (int32_t)mOrderedTypes.size()) {
3441 mOrderedTypes.add();
3442 }
3443 if (mOrderedTypes.itemAt(idx) != NULL) {
3444 sp<Type> ot = mOrderedTypes.itemAt(idx);
3445 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
3446 " identifier 0x%x (%s vs %s).\n"
3447 "%s:%d: Originally defined here.",
3448 idx, String8(ot->getName()).string(),
3449 String8(t->getName()).string(),
3450 ot->getFirstPublicSourcePos().file.string(),
3451 ot->getFirstPublicSourcePos().line);
3452 return UNKNOWN_ERROR;
3453 }
3454 mOrderedTypes.replaceAt(t, idx);
3455 origOrder.removeAt(i);
3456 i--;
3457 N--;
3458 }
3459 }
3460
3461 size_t j=0;
3462 for (i=0; i<N; i++) {
3463 sp<Type> t = origOrder.itemAt(i);
3464 // There will always be enough room for the remaining types.
3465 while (mOrderedTypes.itemAt(j) != NULL) {
3466 j++;
3467 }
3468 mOrderedTypes.replaceAt(t, j);
3469 }
3470
3471 return NO_ERROR;
3472}
3473
3474sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
3475{
3476 sp<Package> p = mPackages.valueFor(package);
3477 if (p == NULL) {
3478 if (mIsAppPackage) {
3479 if (mHaveAppPackage) {
3480 fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
3481 "Use -x to create extended resources.\n");
3482 return NULL;
3483 }
3484 mHaveAppPackage = true;
3485 p = new Package(package, 127);
3486 } else {
3487 p = new Package(package, mNextPackageId);
3488 }
3489 //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
3490 // String8(package).string(), p->getAssignedId());
3491 mPackages.add(package, p);
3492 mOrderedPackages.add(p);
3493 mNextPackageId++;
3494 }
3495 return p;
3496}
3497
3498sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
3499 const String16& type,
3500 const SourcePos& sourcePos,
3501 bool doSetIndex)
3502{
3503 sp<Package> p = getPackage(package);
3504 if (p == NULL) {
3505 return NULL;
3506 }
3507 return p->getType(type, sourcePos, doSetIndex);
3508}
3509
3510sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
3511 const String16& type,
3512 const String16& name,
3513 const SourcePos& sourcePos,
3514 const ResTable_config* config,
3515 bool doSetIndex)
3516{
3517 sp<Type> t = getType(package, type, sourcePos, doSetIndex);
3518 if (t == NULL) {
3519 return NULL;
3520 }
3521 return t->getEntry(name, sourcePos, config, doSetIndex);
3522}
3523
3524sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
3525 const ResTable_config* config) const
3526{
3527 int pid = Res_GETPACKAGE(resID)+1;
3528 const size_t N = mOrderedPackages.size();
3529 size_t i;
3530 sp<Package> p;
3531 for (i=0; i<N; i++) {
3532 sp<Package> check = mOrderedPackages[i];
3533 if (check->getAssignedId() == pid) {
3534 p = check;
3535 break;
3536 }
3537
3538 }
3539 if (p == NULL) {
3540 fprintf(stderr, "WARNING: Package not found for resource #%08x\n", resID);
3541 return NULL;
3542 }
3543
3544 int tid = Res_GETTYPE(resID);
3545 if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
3546 fprintf(stderr, "WARNING: Type not found for resource #%08x\n", resID);
3547 return NULL;
3548 }
3549 sp<Type> t = p->getOrderedTypes()[tid];
3550
3551 int eid = Res_GETENTRY(resID);
3552 if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
3553 fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
3554 return NULL;
3555 }
3556
3557 sp<ConfigList> c = t->getOrderedConfigs()[eid];
3558 if (c == NULL) {
3559 fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
3560 return NULL;
3561 }
3562
3563 ConfigDescription cdesc;
3564 if (config) cdesc = *config;
3565 sp<Entry> e = c->getEntries().valueFor(cdesc);
3566 if (c == NULL) {
3567 fprintf(stderr, "WARNING: Entry configuration not found for resource #%08x\n", resID);
3568 return NULL;
3569 }
3570
3571 return e;
3572}
3573
3574const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
3575{
3576 sp<const Entry> e = getEntry(resID);
3577 if (e == NULL) {
3578 return NULL;
3579 }
3580
3581 const size_t N = e->getBag().size();
3582 for (size_t i=0; i<N; i++) {
3583 const Item& it = e->getBag().valueAt(i);
3584 if (it.bagKeyId == 0) {
3585 fprintf(stderr, "WARNING: ID not yet assigned to '%s' in bag '%s'\n",
3586 String8(e->getName()).string(),
3587 String8(e->getBag().keyAt(i)).string());
3588 }
3589 if (it.bagKeyId == attrID) {
3590 return &it;
3591 }
3592 }
3593
3594 return NULL;
3595}
3596
3597bool ResourceTable::getItemValue(
3598 uint32_t resID, uint32_t attrID, Res_value* outValue)
3599{
3600 const Item* item = getItem(resID, attrID);
3601
3602 bool res = false;
3603 if (item != NULL) {
3604 if (item->evaluating) {
3605 sp<const Entry> e = getEntry(resID);
3606 const size_t N = e->getBag().size();
3607 size_t i;
3608 for (i=0; i<N; i++) {
3609 if (&e->getBag().valueAt(i) == item) {
3610 break;
3611 }
3612 }
3613 fprintf(stderr, "WARNING: Circular reference detected in key '%s' of bag '%s'\n",
3614 String8(e->getName()).string(),
3615 String8(e->getBag().keyAt(i)).string());
3616 return false;
3617 }
3618 item->evaluating = true;
3619 res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
3620 NOISY(
3621 if (res) {
3622 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
3623 resID, attrID, String8(getEntry(resID)->getName()).string(),
3624 outValue->dataType, outValue->data);
3625 } else {
3626 printf("getItemValue of #%08x[#%08x]: failed\n",
3627 resID, attrID);
3628 }
3629 );
3630 item->evaluating = false;
3631 }
3632 return res;
3633}