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