blob: 6e7f3fb950c7df7aa80defde23748cecac11b29b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Build resource files from raw assets.
5//
6
7#include "ResourceTable.h"
8
9#include "XMLNode.h"
Dianne Hackborne6b68032011-10-13 16:26:02 -070010#include "ResourceFilter.h"
Christopher Tated8dde13a2012-11-16 15:58:08 -080011#include "ResourceIdCache.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080012
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080013#include <androidfw/ResourceTypes.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080014#include <utils/ByteOrder.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080015#include <stdarg.h>
16
Andreas Gampe2412f842014-09-30 20:55:57 -070017// SSIZE: mingw does not have signed size_t == ssize_t.
18// STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary.
19#if HAVE_PRINTF_ZD
20# define SSIZE(x) x
21# define STATUST(x) x
22#else
23# define SSIZE(x) (signed size_t)x
24# define STATUST(x) (status_t)x
25#endif
26
27// Set to true for noisy debug output.
28static const bool kIsDebug = false;
29
30#if PRINT_STRING_METRICS
31static const bool kPrintStringMetrics = true;
32#else
33static const bool kPrintStringMetrics = false;
34#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
36status_t compileXmlFile(const sp<AaptAssets>& assets,
37 const sp<AaptFile>& target,
38 ResourceTable* table,
39 int options)
40{
41 sp<XMLNode> root = XMLNode::parse(target);
42 if (root == NULL) {
43 return UNKNOWN_ERROR;
44 }
Anton Krumina2ef5c02014-03-12 14:46:44 -070045
Dianne Hackborna96cbb42009-05-13 15:06:13 -070046 return compileXmlFile(assets, root, target, table, options);
47}
48
49status_t compileXmlFile(const sp<AaptAssets>& assets,
Dianne Hackborncf244ad2010-03-09 15:00:30 -080050 const sp<AaptFile>& target,
51 const sp<AaptFile>& outTarget,
52 ResourceTable* table,
53 int options)
54{
55 sp<XMLNode> root = XMLNode::parse(target);
56 if (root == NULL) {
57 return UNKNOWN_ERROR;
58 }
59
60 return compileXmlFile(assets, root, outTarget, table, options);
61}
62
63status_t compileXmlFile(const sp<AaptAssets>& assets,
Dianne Hackborna96cbb42009-05-13 15:06:13 -070064 const sp<XMLNode>& root,
65 const sp<AaptFile>& target,
66 ResourceTable* table,
67 int options)
68{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) {
70 root->removeWhitespace(true, NULL);
71 } else if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) {
72 root->removeWhitespace(false, NULL);
73 }
74
Kenny Root19138462009-12-04 09:38:48 -080075 if ((options&XML_COMPILE_UTF8) != 0) {
76 root->setUTF8(true);
77 }
78
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 bool hasErrors = false;
80
81 if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
82 status_t err = root->assignResourceIds(assets, table);
83 if (err != NO_ERROR) {
84 hasErrors = true;
85 }
86 }
87
88 status_t err = root->parseValues(assets, table);
89 if (err != NO_ERROR) {
90 hasErrors = true;
91 }
92
93 if (hasErrors) {
94 return UNKNOWN_ERROR;
95 }
Andreas Gampe2412f842014-09-30 20:55:57 -070096
97 if (kIsDebug) {
98 printf("Input XML Resource:\n");
99 root->print();
100 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 err = root->flatten(target,
102 (options&XML_COMPILE_STRIP_COMMENTS) != 0,
103 (options&XML_COMPILE_STRIP_RAW_VALUES) != 0);
104 if (err != NO_ERROR) {
105 return err;
106 }
107
Andreas Gampe2412f842014-09-30 20:55:57 -0700108 if (kIsDebug) {
109 printf("Output XML Resource:\n");
110 ResXMLTree tree;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 tree.setTo(target->getData(), target->getSize());
Andreas Gampe2412f842014-09-30 20:55:57 -0700112 printXMLBlock(&tree);
113 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114
115 target->setCompressionMethod(ZipEntry::kCompressDeflated);
116
117 return err;
118}
119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120struct flag_entry
121{
122 const char16_t* name;
123 size_t nameLen;
124 uint32_t value;
125 const char* description;
126};
127
128static const char16_t referenceArray[] =
129 { 'r', 'e', 'f', 'e', 'r', 'e', 'n', 'c', 'e' };
130static const char16_t stringArray[] =
131 { 's', 't', 'r', 'i', 'n', 'g' };
132static const char16_t integerArray[] =
133 { 'i', 'n', 't', 'e', 'g', 'e', 'r' };
134static const char16_t booleanArray[] =
135 { 'b', 'o', 'o', 'l', 'e', 'a', 'n' };
136static const char16_t colorArray[] =
137 { 'c', 'o', 'l', 'o', 'r' };
138static const char16_t floatArray[] =
139 { 'f', 'l', 'o', 'a', 't' };
140static const char16_t dimensionArray[] =
141 { 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n' };
142static const char16_t fractionArray[] =
143 { 'f', 'r', 'a', 'c', 't', 'i', 'o', 'n' };
144static const char16_t enumArray[] =
145 { 'e', 'n', 'u', 'm' };
146static const char16_t flagsArray[] =
147 { 'f', 'l', 'a', 'g', 's' };
148
149static const flag_entry gFormatFlags[] = {
150 { referenceArray, sizeof(referenceArray)/2, ResTable_map::TYPE_REFERENCE,
151 "a reference to another resource, in the form \"<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>\"\n"
152 "or to a theme attribute in the form \"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\"."},
153 { stringArray, sizeof(stringArray)/2, ResTable_map::TYPE_STRING,
154 "a string value, using '\\\\;' to escape characters such as '\\\\n' or '\\\\uxxxx' for a unicode character." },
155 { integerArray, sizeof(integerArray)/2, ResTable_map::TYPE_INTEGER,
156 "an integer value, such as \"<code>100</code>\"." },
157 { booleanArray, sizeof(booleanArray)/2, ResTable_map::TYPE_BOOLEAN,
158 "a boolean value, either \"<code>true</code>\" or \"<code>false</code>\"." },
159 { colorArray, sizeof(colorArray)/2, ResTable_map::TYPE_COLOR,
160 "a color value, in the form of \"<code>#<i>rgb</i></code>\", \"<code>#<i>argb</i></code>\",\n"
161 "\"<code>#<i>rrggbb</i></code>\", or \"<code>#<i>aarrggbb</i></code>\"." },
162 { floatArray, sizeof(floatArray)/2, ResTable_map::TYPE_FLOAT,
163 "a floating point value, such as \"<code>1.2</code>\"."},
164 { dimensionArray, sizeof(dimensionArray)/2, ResTable_map::TYPE_DIMENSION,
165 "a dimension value, which is a floating point number appended with a unit such as \"<code>14.5sp</code>\".\n"
166 "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n"
167 "in (inches), mm (millimeters)." },
168 { fractionArray, sizeof(fractionArray)/2, ResTable_map::TYPE_FRACTION,
169 "a fractional value, which is a floating point number appended with either % or %p, such as \"<code>14.5%</code>\".\n"
170 "The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to\n"
171 "some parent container." },
172 { enumArray, sizeof(enumArray)/2, ResTable_map::TYPE_ENUM, NULL },
173 { flagsArray, sizeof(flagsArray)/2, ResTable_map::TYPE_FLAGS, NULL },
174 { NULL, 0, 0, NULL }
175};
176
177static const char16_t suggestedArray[] = { 's', 'u', 'g', 'g', 'e', 's', 't', 'e', 'd' };
178
179static const flag_entry l10nRequiredFlags[] = {
180 { suggestedArray, sizeof(suggestedArray)/2, ResTable_map::L10N_SUGGESTED, NULL },
181 { NULL, 0, 0, NULL }
182};
183
184static const char16_t nulStr[] = { 0 };
185
186static uint32_t parse_flags(const char16_t* str, size_t len,
187 const flag_entry* flags, bool* outError = NULL)
188{
189 while (len > 0 && isspace(*str)) {
190 str++;
191 len--;
192 }
193 while (len > 0 && isspace(str[len-1])) {
194 len--;
195 }
196
197 const char16_t* const end = str + len;
198 uint32_t value = 0;
199
200 while (str < end) {
201 const char16_t* div = str;
202 while (div < end && *div != '|') {
203 div++;
204 }
205
206 const flag_entry* cur = flags;
207 while (cur->name) {
208 if (strzcmp16(cur->name, cur->nameLen, str, div-str) == 0) {
209 value |= cur->value;
210 break;
211 }
212 cur++;
213 }
214
215 if (!cur->name) {
216 if (outError) *outError = true;
217 return 0;
218 }
219
220 str = div < end ? div+1 : div;
221 }
222
223 if (outError) *outError = false;
224 return value;
225}
226
227static String16 mayOrMust(int type, int flags)
228{
229 if ((type&(~flags)) == 0) {
230 return String16("<p>Must");
231 }
232
233 return String16("<p>May");
234}
235
236static void appendTypeInfo(ResourceTable* outTable, const String16& pkg,
237 const String16& typeName, const String16& ident, int type,
238 const flag_entry* flags)
239{
240 bool hadType = false;
241 while (flags->name) {
242 if ((type&flags->value) != 0 && flags->description != NULL) {
243 String16 fullMsg(mayOrMust(type, flags->value));
244 fullMsg.append(String16(" be "));
245 fullMsg.append(String16(flags->description));
246 outTable->appendTypeComment(pkg, typeName, ident, fullMsg);
247 hadType = true;
248 }
249 flags++;
250 }
251 if (hadType && (type&ResTable_map::TYPE_REFERENCE) == 0) {
252 outTable->appendTypeComment(pkg, typeName, ident,
253 String16("<p>This may also be a reference to a resource (in the form\n"
254 "\"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>\") or\n"
255 "theme attribute (in the form\n"
256 "\"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\")\n"
257 "containing a value of this type."));
258 }
259}
260
261struct PendingAttribute
262{
263 const String16 myPackage;
264 const SourcePos sourcePos;
265 const bool appendComment;
266 int32_t type;
267 String16 ident;
268 String16 comment;
269 bool hasErrors;
270 bool added;
271
272 PendingAttribute(String16 _package, const sp<AaptFile>& in,
273 ResXMLTree& block, bool _appendComment)
274 : myPackage(_package)
275 , sourcePos(in->getPrintableSource(), block.getLineNumber())
276 , appendComment(_appendComment)
277 , type(ResTable_map::TYPE_ANY)
278 , hasErrors(false)
279 , added(false)
280 {
281 }
282
283 status_t createIfNeeded(ResourceTable* outTable)
284 {
285 if (added || hasErrors) {
286 return NO_ERROR;
287 }
288 added = true;
289
290 String16 attr16("attr");
291
292 if (outTable->hasBagOrEntry(myPackage, attr16, ident)) {
293 sourcePos.error("Attribute \"%s\" has already been defined\n",
294 String8(ident).string());
295 hasErrors = true;
296 return UNKNOWN_ERROR;
297 }
298
299 char numberStr[16];
300 sprintf(numberStr, "%d", type);
301 status_t err = outTable->addBag(sourcePos, myPackage,
302 attr16, ident, String16(""),
303 String16("^type"),
304 String16(numberStr), NULL, NULL);
305 if (err != NO_ERROR) {
306 hasErrors = true;
307 return err;
308 }
309 outTable->appendComment(myPackage, attr16, ident, comment, appendComment);
310 //printf("Attribute %s comment: %s\n", String8(ident).string(),
311 // String8(comment).string());
312 return err;
313 }
314};
315
316static status_t compileAttribute(const sp<AaptFile>& in,
317 ResXMLTree& block,
318 const String16& myPackage,
319 ResourceTable* outTable,
320 String16* outIdent = NULL,
321 bool inStyleable = false)
322{
323 PendingAttribute attr(myPackage, in, block, inStyleable);
324
325 const String16 attr16("attr");
326 const String16 id16("id");
327
328 // Attribute type constants.
329 const String16 enum16("enum");
330 const String16 flag16("flag");
331
332 ResXMLTree::event_code_t code;
333 size_t len;
334 status_t err;
335
336 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
337 if (identIdx >= 0) {
338 attr.ident = String16(block.getAttributeStringValue(identIdx, &len));
339 if (outIdent) {
340 *outIdent = attr.ident;
341 }
342 } else {
343 attr.sourcePos.error("A 'name' attribute is required for <attr>\n");
344 attr.hasErrors = true;
345 }
346
347 attr.comment = String16(
348 block.getComment(&len) ? block.getComment(&len) : nulStr);
349
350 ssize_t typeIdx = block.indexOfAttribute(NULL, "format");
351 if (typeIdx >= 0) {
352 String16 typeStr = String16(block.getAttributeStringValue(typeIdx, &len));
353 attr.type = parse_flags(typeStr.string(), typeStr.size(), gFormatFlags);
354 if (attr.type == 0) {
355 attr.sourcePos.error("Tag <attr> 'format' attribute value \"%s\" not valid\n",
356 String8(typeStr).string());
357 attr.hasErrors = true;
358 }
359 attr.createIfNeeded(outTable);
360 } else if (!inStyleable) {
361 // Attribute definitions outside of styleables always define the
362 // attribute as a generic value.
363 attr.createIfNeeded(outTable);
364 }
365
366 //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).string(), attr.type);
367
368 ssize_t minIdx = block.indexOfAttribute(NULL, "min");
369 if (minIdx >= 0) {
370 String16 val = String16(block.getAttributeStringValue(minIdx, &len));
371 if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
372 attr.sourcePos.error("Tag <attr> 'min' attribute must be a number, not \"%s\"\n",
373 String8(val).string());
374 attr.hasErrors = true;
375 }
376 attr.createIfNeeded(outTable);
377 if (!attr.hasErrors) {
378 err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
379 String16(""), String16("^min"), String16(val), NULL, NULL);
380 if (err != NO_ERROR) {
381 attr.hasErrors = true;
382 }
383 }
384 }
385
386 ssize_t maxIdx = block.indexOfAttribute(NULL, "max");
387 if (maxIdx >= 0) {
388 String16 val = String16(block.getAttributeStringValue(maxIdx, &len));
389 if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
390 attr.sourcePos.error("Tag <attr> 'max' attribute must be a number, not \"%s\"\n",
391 String8(val).string());
392 attr.hasErrors = true;
393 }
394 attr.createIfNeeded(outTable);
395 if (!attr.hasErrors) {
396 err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
397 String16(""), String16("^max"), String16(val), NULL, NULL);
398 attr.hasErrors = true;
399 }
400 }
401
402 if ((minIdx >= 0 || maxIdx >= 0) && (attr.type&ResTable_map::TYPE_INTEGER) == 0) {
403 attr.sourcePos.error("Tag <attr> must have format=integer attribute if using max or min\n");
404 attr.hasErrors = true;
405 }
406
407 ssize_t l10nIdx = block.indexOfAttribute(NULL, "localization");
408 if (l10nIdx >= 0) {
Dan Albertf348c152014-09-08 18:28:00 -0700409 const char16_t* str = block.getAttributeStringValue(l10nIdx, &len);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 bool error;
411 uint32_t l10n_required = parse_flags(str, len, l10nRequiredFlags, &error);
412 if (error) {
413 attr.sourcePos.error("Tag <attr> 'localization' attribute value \"%s\" not valid\n",
414 String8(str).string());
415 attr.hasErrors = true;
416 }
417 attr.createIfNeeded(outTable);
418 if (!attr.hasErrors) {
Kenny Rootf5a7c122010-02-18 09:43:08 -0800419 char buf[11];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 sprintf(buf, "%d", l10n_required);
421 err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
422 String16(""), String16("^l10n"), String16(buf), NULL, NULL);
423 if (err != NO_ERROR) {
424 attr.hasErrors = true;
425 }
426 }
427 }
428
429 String16 enumOrFlagsComment;
430
431 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
432 if (code == ResXMLTree::START_TAG) {
433 uint32_t localType = 0;
434 if (strcmp16(block.getElementName(&len), enum16.string()) == 0) {
435 localType = ResTable_map::TYPE_ENUM;
436 } else if (strcmp16(block.getElementName(&len), flag16.string()) == 0) {
437 localType = ResTable_map::TYPE_FLAGS;
438 } else {
439 SourcePos(in->getPrintableSource(), block.getLineNumber())
440 .error("Tag <%s> can not appear inside <attr>, only <enum> or <flag>\n",
441 String8(block.getElementName(&len)).string());
442 return UNKNOWN_ERROR;
443 }
444
445 attr.createIfNeeded(outTable);
446
447 if (attr.type == ResTable_map::TYPE_ANY) {
448 // No type was explicitly stated, so supplying enum tags
449 // implicitly creates an enum or flag.
450 attr.type = 0;
451 }
452
453 if ((attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) == 0) {
454 // Wasn't originally specified as an enum, so update its type.
455 attr.type |= localType;
456 if (!attr.hasErrors) {
457 char numberStr[16];
458 sprintf(numberStr, "%d", attr.type);
459 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
460 myPackage, attr16, attr.ident, String16(""),
461 String16("^type"), String16(numberStr), NULL, NULL, true);
462 if (err != NO_ERROR) {
463 attr.hasErrors = true;
464 }
465 }
466 } else if ((uint32_t)(attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) != localType) {
467 if (localType == ResTable_map::TYPE_ENUM) {
468 SourcePos(in->getPrintableSource(), block.getLineNumber())
469 .error("<enum> attribute can not be used inside a flags format\n");
470 attr.hasErrors = true;
471 } else {
472 SourcePos(in->getPrintableSource(), block.getLineNumber())
473 .error("<flag> attribute can not be used inside a enum format\n");
474 attr.hasErrors = true;
475 }
476 }
477
478 String16 itemIdent;
479 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
480 if (itemIdentIdx >= 0) {
481 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
482 } else {
483 SourcePos(in->getPrintableSource(), block.getLineNumber())
484 .error("A 'name' attribute is required for <enum> or <flag>\n");
485 attr.hasErrors = true;
486 }
487
488 String16 value;
489 ssize_t valueIdx = block.indexOfAttribute(NULL, "value");
490 if (valueIdx >= 0) {
491 value = String16(block.getAttributeStringValue(valueIdx, &len));
492 } else {
493 SourcePos(in->getPrintableSource(), block.getLineNumber())
494 .error("A 'value' attribute is required for <enum> or <flag>\n");
495 attr.hasErrors = true;
496 }
497 if (!attr.hasErrors && !ResTable::stringToInt(value.string(), value.size(), NULL)) {
498 SourcePos(in->getPrintableSource(), block.getLineNumber())
499 .error("Tag <enum> or <flag> 'value' attribute must be a number,"
500 " not \"%s\"\n",
501 String8(value).string());
502 attr.hasErrors = true;
503 }
504
505 // Make sure an id is defined for this enum/flag identifier...
506 if (!attr.hasErrors && !outTable->hasBagOrEntry(itemIdent, &id16, &myPackage)) {
507 err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
508 myPackage, id16, itemIdent, String16(), NULL);
509 if (err != NO_ERROR) {
510 attr.hasErrors = true;
511 }
512 }
513
514 if (!attr.hasErrors) {
515 if (enumOrFlagsComment.size() == 0) {
516 enumOrFlagsComment.append(mayOrMust(attr.type,
517 ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS));
518 enumOrFlagsComment.append((attr.type&ResTable_map::TYPE_ENUM)
519 ? String16(" be one of the following constant values.")
520 : String16(" be one or more (separated by '|') of the following constant values."));
Dirk Dougherty59ad2752009-11-03 15:33:37 -0800521 enumOrFlagsComment.append(String16("</p>\n<table>\n"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 "<colgroup align=\"left\" />\n"
523 "<colgroup align=\"left\" />\n"
524 "<colgroup align=\"left\" />\n"
Dirk Dougherty59ad2752009-11-03 15:33:37 -0800525 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 }
527
Dirk Dougherty59ad2752009-11-03 15:33:37 -0800528 enumOrFlagsComment.append(String16("\n<tr><td><code>"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 enumOrFlagsComment.append(itemIdent);
Dirk Dougherty59ad2752009-11-03 15:33:37 -0800530 enumOrFlagsComment.append(String16("</code></td><td>"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 enumOrFlagsComment.append(value);
Dirk Dougherty59ad2752009-11-03 15:33:37 -0800532 enumOrFlagsComment.append(String16("</td><td>"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 if (block.getComment(&len)) {
534 enumOrFlagsComment.append(String16(block.getComment(&len)));
535 }
Dirk Dougherty59ad2752009-11-03 15:33:37 -0800536 enumOrFlagsComment.append(String16("</td></tr>"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537
538 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
539 myPackage,
540 attr16, attr.ident, String16(""),
541 itemIdent, value, NULL, NULL, false, true);
542 if (err != NO_ERROR) {
543 attr.hasErrors = true;
544 }
545 }
546 } else if (code == ResXMLTree::END_TAG) {
547 if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
548 break;
549 }
550 if ((attr.type&ResTable_map::TYPE_ENUM) != 0) {
551 if (strcmp16(block.getElementName(&len), enum16.string()) != 0) {
552 SourcePos(in->getPrintableSource(), block.getLineNumber())
553 .error("Found tag </%s> where </enum> is expected\n",
554 String8(block.getElementName(&len)).string());
555 return UNKNOWN_ERROR;
556 }
557 } else {
558 if (strcmp16(block.getElementName(&len), flag16.string()) != 0) {
559 SourcePos(in->getPrintableSource(), block.getLineNumber())
560 .error("Found tag </%s> where </flag> is expected\n",
561 String8(block.getElementName(&len)).string());
562 return UNKNOWN_ERROR;
563 }
564 }
565 }
566 }
567
568 if (!attr.hasErrors && attr.added) {
569 appendTypeInfo(outTable, myPackage, attr16, attr.ident, attr.type, gFormatFlags);
570 }
571
572 if (!attr.hasErrors && enumOrFlagsComment.size() > 0) {
573 enumOrFlagsComment.append(String16("\n</table>"));
574 outTable->appendTypeComment(myPackage, attr16, attr.ident, enumOrFlagsComment);
575 }
576
577
578 return NO_ERROR;
579}
580
581bool localeIsDefined(const ResTable_config& config)
582{
583 return config.locale == 0;
584}
585
586status_t parseAndAddBag(Bundle* bundle,
587 const sp<AaptFile>& in,
588 ResXMLTree* block,
589 const ResTable_config& config,
590 const String16& myPackage,
591 const String16& curType,
592 const String16& ident,
593 const String16& parentIdent,
594 const String16& itemIdent,
595 int32_t curFormat,
Kenny Root15fe2cb2010-05-28 15:44:32 -0700596 bool isFormatted,
Andreas Gampe2412f842014-09-30 20:55:57 -0700597 const String16& /* product */,
Anton Krumina2ef5c02014-03-12 14:46:44 -0700598 PseudolocalizationMethod pseudolocalize,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 const bool overwrite,
600 ResourceTable* outTable)
601{
602 status_t err;
603 const String16 item16("item");
Anton Krumina2ef5c02014-03-12 14:46:44 -0700604
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 String16 str;
606 Vector<StringPool::entry_style_span> spans;
607 err = parseStyledString(bundle, in->getPrintableSource().string(),
Kenny Root15fe2cb2010-05-28 15:44:32 -0700608 block, item16, &str, &spans, isFormatted,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 pseudolocalize);
610 if (err != NO_ERROR) {
611 return err;
612 }
Andreas Gampe2412f842014-09-30 20:55:57 -0700613
614 if (kIsDebug) {
615 printf("Adding resource bag entry l=%c%c c=%c%c orien=%d d=%d "
616 " pid=%s, bag=%s, id=%s: %s\n",
617 config.language[0], config.language[1],
618 config.country[0], config.country[1],
619 config.orientation, config.density,
620 String8(parentIdent).string(),
621 String8(ident).string(),
622 String8(itemIdent).string(),
623 String8(str).string());
624 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625
626 err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()),
627 myPackage, curType, ident, parentIdent, itemIdent, str,
628 &spans, &config, overwrite, false, curFormat);
629 return err;
630}
631
Eric Fischer90964042010-09-15 15:59:21 -0700632/*
633 * Returns true if needle is one of the elements in the comma-separated list
634 * haystack, false otherwise.
635 */
636bool isInProductList(const String16& needle, const String16& haystack) {
637 const char16_t *needle2 = needle.string();
638 const char16_t *haystack2 = haystack.string();
639 size_t needlesize = needle.size();
640
641 while (*haystack2 != '\0') {
642 if (strncmp16(haystack2, needle2, needlesize) == 0) {
643 if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') {
644 return true;
645 }
646 }
647
648 while (*haystack2 != '\0' && *haystack2 != ',') {
649 haystack2++;
650 }
651 if (*haystack2 == ',') {
652 haystack2++;
653 }
654 }
655
656 return false;
657}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658
Adam Lesinski2b8e82f2013-10-04 12:06:38 -0700659/*
660 * A simple container that holds a resource type and name. It is ordered first by type then
661 * by name.
662 */
663struct type_ident_pair_t {
664 String16 type;
665 String16 ident;
666
667 type_ident_pair_t() { };
668 type_ident_pair_t(const String16& t, const String16& i) : type(t), ident(i) { }
669 type_ident_pair_t(const type_ident_pair_t& o) : type(o.type), ident(o.ident) { }
670 inline bool operator < (const type_ident_pair_t& o) const {
671 int cmp = compare_type(type, o.type);
672 if (cmp < 0) {
673 return true;
674 } else if (cmp > 0) {
675 return false;
676 } else {
677 return strictly_order_type(ident, o.ident);
678 }
679 }
680};
681
682
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683status_t parseAndAddEntry(Bundle* bundle,
684 const sp<AaptFile>& in,
685 ResXMLTree* block,
686 const ResTable_config& config,
687 const String16& myPackage,
688 const String16& curType,
689 const String16& ident,
690 const String16& curTag,
691 bool curIsStyled,
692 int32_t curFormat,
Kenny Root15fe2cb2010-05-28 15:44:32 -0700693 bool isFormatted,
Eric Fischer90964042010-09-15 15:59:21 -0700694 const String16& product,
Anton Krumina2ef5c02014-03-12 14:46:44 -0700695 PseudolocalizationMethod pseudolocalize,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 const bool overwrite,
Adam Lesinski2b8e82f2013-10-04 12:06:38 -0700697 KeyedVector<type_ident_pair_t, bool>* skippedResourceNames,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 ResourceTable* outTable)
699{
700 status_t err;
701
702 String16 str;
703 Vector<StringPool::entry_style_span> spans;
704 err = parseStyledString(bundle, in->getPrintableSource().string(), block,
705 curTag, &str, curIsStyled ? &spans : NULL,
Kenny Root15fe2cb2010-05-28 15:44:32 -0700706 isFormatted, pseudolocalize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707
708 if (err < NO_ERROR) {
709 return err;
710 }
711
Eric Fischer90964042010-09-15 15:59:21 -0700712 /*
713 * If a product type was specified on the command line
714 * and also in the string, and the two are not the same,
715 * return without adding the string.
716 */
717
718 const char *bundleProduct = bundle->getProduct();
719 if (bundleProduct == NULL) {
720 bundleProduct = "";
721 }
722
723 if (product.size() != 0) {
724 /*
725 * If the command-line-specified product is empty, only "default"
726 * matches. Other variants are skipped. This is so generation
727 * of the R.java file when the product is not known is predictable.
728 */
729
730 if (bundleProduct[0] == '\0') {
731 if (strcmp16(String16("default").string(), product.string()) != 0) {
Adam Lesinski2b8e82f2013-10-04 12:06:38 -0700732 /*
733 * This string has a product other than 'default'. Do not add it,
734 * but record it so that if we do not see the same string with
735 * product 'default' or no product, then report an error.
736 */
737 skippedResourceNames->replaceValueFor(
738 type_ident_pair_t(curType, ident), true);
Eric Fischer90964042010-09-15 15:59:21 -0700739 return NO_ERROR;
740 }
741 } else {
742 /*
743 * The command-line product is not empty.
744 * If the product for this string is on the command-line list,
745 * it matches. "default" also matches, but only if nothing
746 * else has matched already.
747 */
748
749 if (isInProductList(product, String16(bundleProduct))) {
750 ;
751 } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
Eric Fischer914f7e62011-09-27 16:09:41 -0700752 !outTable->hasBagOrEntry(myPackage, curType, ident, config)) {
Eric Fischer90964042010-09-15 15:59:21 -0700753 ;
754 } else {
755 return NO_ERROR;
756 }
757 }
758 }
759
Andreas Gampe2412f842014-09-30 20:55:57 -0700760 if (kIsDebug) {
761 printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n",
762 config.language[0], config.language[1],
763 config.country[0], config.country[1],
764 config.orientation, config.density,
765 String8(ident).string(), String8(str).string());
766 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767
768 err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
769 myPackage, curType, ident, str, &spans, &config,
770 false, curFormat, overwrite);
771
772 return err;
773}
774
775status_t compileResourceFile(Bundle* bundle,
776 const sp<AaptAssets>& assets,
777 const sp<AaptFile>& in,
778 const ResTable_config& defParams,
779 const bool overwrite,
780 ResourceTable* outTable)
781{
782 ResXMLTree block;
783 status_t err = parseXMLResource(in, &block, false, true);
784 if (err != NO_ERROR) {
785 return err;
786 }
787
788 // Top-level tag.
789 const String16 resources16("resources");
790
791 // Identifier declaration tags.
792 const String16 declare_styleable16("declare-styleable");
793 const String16 attr16("attr");
794
795 // Data creation organizational tags.
796 const String16 string16("string");
797 const String16 drawable16("drawable");
798 const String16 color16("color");
799 const String16 bool16("bool");
800 const String16 integer16("integer");
801 const String16 dimen16("dimen");
802 const String16 fraction16("fraction");
803 const String16 style16("style");
804 const String16 plurals16("plurals");
805 const String16 array16("array");
806 const String16 string_array16("string-array");
807 const String16 integer_array16("integer-array");
808 const String16 public16("public");
Dianne Hackbornf479aa02009-05-20 16:01:06 -0700809 const String16 public_padding16("public-padding");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 const String16 private_symbols16("private-symbols");
Dianne Hackborn1644c6d72012-02-06 15:33:21 -0800811 const String16 java_symbol16("java-symbol");
Dianne Hackborn58c27a02009-08-13 13:36:00 -0700812 const String16 add_resource16("add-resource");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 const String16 skip16("skip");
814 const String16 eat_comment16("eat-comment");
815
816 // Data creation tags.
817 const String16 bag16("bag");
818 const String16 item16("item");
819
820 // Attribute type constants.
821 const String16 enum16("enum");
822
823 // plural values
824 const String16 other16("other");
825 const String16 quantityOther16("^other");
826 const String16 zero16("zero");
827 const String16 quantityZero16("^zero");
828 const String16 one16("one");
829 const String16 quantityOne16("^one");
830 const String16 two16("two");
831 const String16 quantityTwo16("^two");
832 const String16 few16("few");
833 const String16 quantityFew16("^few");
834 const String16 many16("many");
835 const String16 quantityMany16("^many");
836
837 // useful attribute names and special values
838 const String16 name16("name");
839 const String16 translatable16("translatable");
Kenny Root15fe2cb2010-05-28 15:44:32 -0700840 const String16 formatted16("formatted");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 const String16 false16("false");
842
843 const String16 myPackage(assets->getPackage());
844
845 bool hasErrors = false;
Kenny Root15fe2cb2010-05-28 15:44:32 -0700846
847 bool fileIsTranslatable = true;
848 if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) {
849 fileIsTranslatable = false;
850 }
851
Dianne Hackbornf479aa02009-05-20 16:01:06 -0700852 DefaultKeyedVector<String16, uint32_t> nextPublicId(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853
Adam Lesinski2b8e82f2013-10-04 12:06:38 -0700854 // Stores the resource names that were skipped. Typically this happens when
855 // AAPT is invoked without a product specified and a resource has no
856 // 'default' product attribute.
857 KeyedVector<type_ident_pair_t, bool> skippedResourceNames;
858
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 ResXMLTree::event_code_t code;
860 do {
861 code = block.next();
862 } while (code == ResXMLTree::START_NAMESPACE);
863
864 size_t len;
865 if (code != ResXMLTree::START_TAG) {
866 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
867 "No start tag found\n");
868 return UNKNOWN_ERROR;
869 }
870 if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
871 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
872 "Invalid start tag %s\n", String8(block.getElementName(&len)).string());
873 return UNKNOWN_ERROR;
874 }
875
876 ResTable_config curParams(defParams);
877
878 ResTable_config pseudoParams(curParams);
Anton Krumina2ef5c02014-03-12 14:46:44 -0700879 pseudoParams.language[0] = 'e';
880 pseudoParams.language[1] = 'n';
881 pseudoParams.country[0] = 'X';
882 pseudoParams.country[1] = 'A';
883
884 ResTable_config pseudoBidiParams(curParams);
885 pseudoBidiParams.language[0] = 'a';
886 pseudoBidiParams.language[1] = 'r';
887 pseudoBidiParams.country[0] = 'X';
888 pseudoBidiParams.country[1] = 'B';
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889
Igor Viarheichyk47843df2014-05-01 17:04:39 -0700890 // We should skip resources for pseudolocales if they were
891 // already added automatically. This is a fix for a transition period when
892 // manually pseudolocalized resources may be expected.
893 // TODO: remove this check after next SDK version release.
894 if ((bundle->getPseudolocalize() & PSEUDO_ACCENTED &&
895 curParams.locale == pseudoParams.locale) ||
896 (bundle->getPseudolocalize() & PSEUDO_BIDI &&
897 curParams.locale == pseudoBidiParams.locale)) {
898 SourcePos(in->getPrintableSource(), 0).warning(
899 "Resource file %s is skipped as pseudolocalization"
900 " was done automatically.",
901 in->getPrintableSource().string());
902 return NO_ERROR;
903 }
904
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
906 if (code == ResXMLTree::START_TAG) {
907 const String16* curTag = NULL;
908 String16 curType;
909 int32_t curFormat = ResTable_map::TYPE_ANY;
910 bool curIsBag = false;
Robert Greenwalt1aa81702009-06-05 15:59:15 -0700911 bool curIsBagReplaceOnOverwrite = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800912 bool curIsStyled = false;
913 bool curIsPseudolocalizable = false;
Kenny Root15fe2cb2010-05-28 15:44:32 -0700914 bool curIsFormatted = fileIsTranslatable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 bool localHasErrors = false;
916
917 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
918 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
919 && code != ResXMLTree::BAD_DOCUMENT) {
920 if (code == ResXMLTree::END_TAG) {
921 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
922 break;
923 }
924 }
925 }
926 continue;
927
928 } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
929 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
930 && code != ResXMLTree::BAD_DOCUMENT) {
931 if (code == ResXMLTree::END_TAG) {
932 if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
933 break;
934 }
935 }
936 }
937 continue;
938
939 } else if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
940 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
941
942 String16 type;
943 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
944 if (typeIdx < 0) {
945 srcPos.error("A 'type' attribute is required for <public>\n");
946 hasErrors = localHasErrors = true;
947 }
948 type = String16(block.getAttributeStringValue(typeIdx, &len));
949
950 String16 name;
951 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
952 if (nameIdx < 0) {
953 srcPos.error("A 'name' attribute is required for <public>\n");
954 hasErrors = localHasErrors = true;
955 }
956 name = String16(block.getAttributeStringValue(nameIdx, &len));
957
958 uint32_t ident = 0;
959 ssize_t identIdx = block.indexOfAttribute(NULL, "id");
960 if (identIdx >= 0) {
961 const char16_t* identStr = block.getAttributeStringValue(identIdx, &len);
962 Res_value identValue;
963 if (!ResTable::stringToInt(identStr, len, &identValue)) {
964 srcPos.error("Given 'id' attribute is not an integer: %s\n",
965 String8(block.getAttributeStringValue(identIdx, &len)).string());
966 hasErrors = localHasErrors = true;
967 } else {
968 ident = identValue.data;
Dianne Hackbornf479aa02009-05-20 16:01:06 -0700969 nextPublicId.replaceValueFor(type, ident+1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 }
Dianne Hackbornf479aa02009-05-20 16:01:06 -0700971 } else if (nextPublicId.indexOfKey(type) < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 srcPos.error("No 'id' attribute supplied <public>,"
973 " and no previous id defined in this file.\n");
974 hasErrors = localHasErrors = true;
975 } else if (!localHasErrors) {
Dianne Hackbornf479aa02009-05-20 16:01:06 -0700976 ident = nextPublicId.valueFor(type);
977 nextPublicId.replaceValueFor(type, ident+1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 }
979
980 if (!localHasErrors) {
981 err = outTable->addPublic(srcPos, myPackage, type, name, ident);
982 if (err < NO_ERROR) {
983 hasErrors = localHasErrors = true;
984 }
985 }
986 if (!localHasErrors) {
987 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
988 if (symbols != NULL) {
989 symbols = symbols->addNestedSymbol(String8(type), srcPos);
990 }
991 if (symbols != NULL) {
992 symbols->makeSymbolPublic(String8(name), srcPos);
993 String16 comment(
994 block.getComment(&len) ? block.getComment(&len) : nulStr);
995 symbols->appendComment(String8(name), comment, srcPos);
996 } else {
997 srcPos.error("Unable to create symbols!\n");
998 hasErrors = localHasErrors = true;
999 }
1000 }
1001
1002 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1003 if (code == ResXMLTree::END_TAG) {
1004 if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
1005 break;
1006 }
1007 }
1008 }
1009 continue;
1010
Dianne Hackbornf479aa02009-05-20 16:01:06 -07001011 } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
1012 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
1013
1014 String16 type;
1015 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
1016 if (typeIdx < 0) {
1017 srcPos.error("A 'type' attribute is required for <public-padding>\n");
1018 hasErrors = localHasErrors = true;
1019 }
1020 type = String16(block.getAttributeStringValue(typeIdx, &len));
1021
1022 String16 name;
1023 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
1024 if (nameIdx < 0) {
1025 srcPos.error("A 'name' attribute is required for <public-padding>\n");
1026 hasErrors = localHasErrors = true;
1027 }
1028 name = String16(block.getAttributeStringValue(nameIdx, &len));
1029
1030 uint32_t start = 0;
1031 ssize_t startIdx = block.indexOfAttribute(NULL, "start");
1032 if (startIdx >= 0) {
1033 const char16_t* startStr = block.getAttributeStringValue(startIdx, &len);
1034 Res_value startValue;
1035 if (!ResTable::stringToInt(startStr, len, &startValue)) {
1036 srcPos.error("Given 'start' attribute is not an integer: %s\n",
1037 String8(block.getAttributeStringValue(startIdx, &len)).string());
1038 hasErrors = localHasErrors = true;
1039 } else {
1040 start = startValue.data;
1041 }
1042 } else if (nextPublicId.indexOfKey(type) < 0) {
1043 srcPos.error("No 'start' attribute supplied <public-padding>,"
1044 " and no previous id defined in this file.\n");
1045 hasErrors = localHasErrors = true;
1046 } else if (!localHasErrors) {
1047 start = nextPublicId.valueFor(type);
1048 }
1049
1050 uint32_t end = 0;
1051 ssize_t endIdx = block.indexOfAttribute(NULL, "end");
1052 if (endIdx >= 0) {
1053 const char16_t* endStr = block.getAttributeStringValue(endIdx, &len);
1054 Res_value endValue;
1055 if (!ResTable::stringToInt(endStr, len, &endValue)) {
1056 srcPos.error("Given 'end' attribute is not an integer: %s\n",
1057 String8(block.getAttributeStringValue(endIdx, &len)).string());
1058 hasErrors = localHasErrors = true;
1059 } else {
1060 end = endValue.data;
1061 }
1062 } else {
1063 srcPos.error("No 'end' attribute supplied <public-padding>\n");
1064 hasErrors = localHasErrors = true;
1065 }
1066
1067 if (end >= start) {
1068 nextPublicId.replaceValueFor(type, end+1);
1069 } else {
1070 srcPos.error("Padding start '%ul' is after end '%ul'\n",
1071 start, end);
1072 hasErrors = localHasErrors = true;
1073 }
1074
1075 String16 comment(
1076 block.getComment(&len) ? block.getComment(&len) : nulStr);
1077 for (uint32_t curIdent=start; curIdent<=end; curIdent++) {
1078 if (localHasErrors) {
1079 break;
1080 }
1081 String16 curName(name);
1082 char buf[64];
1083 sprintf(buf, "%d", (int)(end-curIdent+1));
1084 curName.append(String16(buf));
1085
1086 err = outTable->addEntry(srcPos, myPackage, type, curName,
1087 String16("padding"), NULL, &curParams, false,
1088 ResTable_map::TYPE_STRING, overwrite);
1089 if (err < NO_ERROR) {
1090 hasErrors = localHasErrors = true;
1091 break;
1092 }
1093 err = outTable->addPublic(srcPos, myPackage, type,
1094 curName, curIdent);
1095 if (err < NO_ERROR) {
1096 hasErrors = localHasErrors = true;
1097 break;
1098 }
1099 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
1100 if (symbols != NULL) {
1101 symbols = symbols->addNestedSymbol(String8(type), srcPos);
1102 }
1103 if (symbols != NULL) {
1104 symbols->makeSymbolPublic(String8(curName), srcPos);
1105 symbols->appendComment(String8(curName), comment, srcPos);
1106 } else {
1107 srcPos.error("Unable to create symbols!\n");
1108 hasErrors = localHasErrors = true;
1109 }
1110 }
1111
1112 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1113 if (code == ResXMLTree::END_TAG) {
1114 if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
1115 break;
1116 }
1117 }
1118 }
1119 continue;
1120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
1122 String16 pkg;
1123 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
1124 if (pkgIdx < 0) {
1125 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1126 "A 'package' attribute is required for <private-symbols>\n");
1127 hasErrors = localHasErrors = true;
1128 }
1129 pkg = String16(block.getAttributeStringValue(pkgIdx, &len));
1130 if (!localHasErrors) {
1131 assets->setSymbolsPrivatePackage(String8(pkg));
1132 }
1133
1134 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1135 if (code == ResXMLTree::END_TAG) {
1136 if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
1137 break;
1138 }
1139 }
1140 }
1141 continue;
1142
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001143 } else if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
1144 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
1145
1146 String16 type;
1147 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
1148 if (typeIdx < 0) {
1149 srcPos.error("A 'type' attribute is required for <public>\n");
1150 hasErrors = localHasErrors = true;
1151 }
1152 type = String16(block.getAttributeStringValue(typeIdx, &len));
1153
1154 String16 name;
1155 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
1156 if (nameIdx < 0) {
1157 srcPos.error("A 'name' attribute is required for <public>\n");
1158 hasErrors = localHasErrors = true;
1159 }
1160 name = String16(block.getAttributeStringValue(nameIdx, &len));
1161
1162 sp<AaptSymbols> symbols = assets->getJavaSymbolsFor(String8("R"));
1163 if (symbols != NULL) {
1164 symbols = symbols->addNestedSymbol(String8(type), srcPos);
1165 }
1166 if (symbols != NULL) {
1167 symbols->makeSymbolJavaSymbol(String8(name), srcPos);
1168 String16 comment(
1169 block.getComment(&len) ? block.getComment(&len) : nulStr);
1170 symbols->appendComment(String8(name), comment, srcPos);
1171 } else {
1172 srcPos.error("Unable to create symbols!\n");
1173 hasErrors = localHasErrors = true;
1174 }
1175
1176 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1177 if (code == ResXMLTree::END_TAG) {
1178 if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
1179 break;
1180 }
1181 }
1182 }
1183 continue;
1184
1185
Dianne Hackborn58c27a02009-08-13 13:36:00 -07001186 } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
1187 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
1188
1189 String16 typeName;
1190 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
1191 if (typeIdx < 0) {
1192 srcPos.error("A 'type' attribute is required for <add-resource>\n");
1193 hasErrors = localHasErrors = true;
1194 }
1195 typeName = String16(block.getAttributeStringValue(typeIdx, &len));
1196
1197 String16 name;
1198 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
1199 if (nameIdx < 0) {
1200 srcPos.error("A 'name' attribute is required for <add-resource>\n");
1201 hasErrors = localHasErrors = true;
1202 }
1203 name = String16(block.getAttributeStringValue(nameIdx, &len));
1204
1205 outTable->canAddEntry(srcPos, myPackage, typeName, name);
1206
1207 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1208 if (code == ResXMLTree::END_TAG) {
Robert Greenwalt93d72512009-09-02 18:23:57 -07001209 if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
Dianne Hackborn58c27a02009-08-13 13:36:00 -07001210 break;
1211 }
1212 }
1213 }
1214 continue;
1215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
1217 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
1218
1219 String16 ident;
1220 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
1221 if (identIdx < 0) {
1222 srcPos.error("A 'name' attribute is required for <declare-styleable>\n");
1223 hasErrors = localHasErrors = true;
1224 }
1225 ident = String16(block.getAttributeStringValue(identIdx, &len));
1226
1227 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
1228 if (!localHasErrors) {
1229 if (symbols != NULL) {
1230 symbols = symbols->addNestedSymbol(String8("styleable"), srcPos);
1231 }
1232 sp<AaptSymbols> styleSymbols = symbols;
1233 if (symbols != NULL) {
1234 symbols = symbols->addNestedSymbol(String8(ident), srcPos);
1235 }
1236 if (symbols == NULL) {
1237 srcPos.error("Unable to create symbols!\n");
1238 return UNKNOWN_ERROR;
1239 }
1240
1241 String16 comment(
1242 block.getComment(&len) ? block.getComment(&len) : nulStr);
1243 styleSymbols->appendComment(String8(ident), comment, srcPos);
1244 } else {
1245 symbols = NULL;
1246 }
1247
1248 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1249 if (code == ResXMLTree::START_TAG) {
1250 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
1251 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1252 && code != ResXMLTree::BAD_DOCUMENT) {
1253 if (code == ResXMLTree::END_TAG) {
1254 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
1255 break;
1256 }
1257 }
1258 }
1259 continue;
1260 } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
1261 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1262 && code != ResXMLTree::BAD_DOCUMENT) {
1263 if (code == ResXMLTree::END_TAG) {
1264 if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
1265 break;
1266 }
1267 }
1268 }
1269 continue;
1270 } else if (strcmp16(block.getElementName(&len), attr16.string()) != 0) {
1271 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1272 "Tag <%s> can not appear inside <declare-styleable>, only <attr>\n",
1273 String8(block.getElementName(&len)).string());
1274 return UNKNOWN_ERROR;
1275 }
1276
1277 String16 comment(
1278 block.getComment(&len) ? block.getComment(&len) : nulStr);
1279 String16 itemIdent;
1280 err = compileAttribute(in, block, myPackage, outTable, &itemIdent, true);
1281 if (err != NO_ERROR) {
1282 hasErrors = localHasErrors = true;
1283 }
1284
1285 if (symbols != NULL) {
1286 SourcePos srcPos(String8(in->getPrintableSource()), block.getLineNumber());
1287 symbols->addSymbol(String8(itemIdent), 0, srcPos);
1288 symbols->appendComment(String8(itemIdent), comment, srcPos);
1289 //printf("Attribute %s comment: %s\n", String8(itemIdent).string(),
1290 // String8(comment).string());
1291 }
1292 } else if (code == ResXMLTree::END_TAG) {
1293 if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
1294 break;
1295 }
1296
1297 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1298 "Found tag </%s> where </attr> is expected\n",
1299 String8(block.getElementName(&len)).string());
1300 return UNKNOWN_ERROR;
1301 }
1302 }
1303 continue;
1304
1305 } else if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
1306 err = compileAttribute(in, block, myPackage, outTable, NULL);
1307 if (err != NO_ERROR) {
1308 hasErrors = true;
1309 }
1310 continue;
1311
1312 } else if (strcmp16(block.getElementName(&len), item16.string()) == 0) {
1313 curTag = &item16;
1314 ssize_t attri = block.indexOfAttribute(NULL, "type");
1315 if (attri >= 0) {
1316 curType = String16(block.getAttributeStringValue(attri, &len));
1317 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
1318 if (formatIdx >= 0) {
1319 String16 formatStr = String16(block.getAttributeStringValue(
1320 formatIdx, &len));
1321 curFormat = parse_flags(formatStr.string(), formatStr.size(),
1322 gFormatFlags);
1323 if (curFormat == 0) {
1324 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1325 "Tag <item> 'format' attribute value \"%s\" not valid\n",
1326 String8(formatStr).string());
1327 hasErrors = localHasErrors = true;
1328 }
1329 }
1330 } else {
1331 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1332 "A 'type' attribute is required for <item>\n");
1333 hasErrors = localHasErrors = true;
1334 }
1335 curIsStyled = true;
1336 } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
1337 // Note the existence and locale of every string we process
Narayan Kamath788fa412014-01-21 15:32:36 +00001338 char rawLocale[RESTABLE_MAX_LOCALE_LEN];
1339 curParams.getBcp47Locale(rawLocale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 String8 locale(rawLocale);
1341 String16 name;
1342 String16 translatable;
Kenny Root15fe2cb2010-05-28 15:44:32 -07001343 String16 formatted;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344
1345 size_t n = block.getAttributeCount();
1346 for (size_t i = 0; i < n; i++) {
1347 size_t length;
Dan Albertf348c152014-09-08 18:28:00 -07001348 const char16_t* attr = block.getAttributeName(i, &length);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 if (strcmp16(attr, name16.string()) == 0) {
1350 name.setTo(block.getAttributeStringValue(i, &length));
1351 } else if (strcmp16(attr, translatable16.string()) == 0) {
1352 translatable.setTo(block.getAttributeStringValue(i, &length));
Kenny Root15fe2cb2010-05-28 15:44:32 -07001353 } else if (strcmp16(attr, formatted16.string()) == 0) {
1354 formatted.setTo(block.getAttributeStringValue(i, &length));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 }
1356 }
1357
1358 if (name.size() > 0) {
1359 if (translatable == false16) {
Kenny Root15fe2cb2010-05-28 15:44:32 -07001360 curIsFormatted = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 // Untranslatable strings must only exist in the default [empty] locale
1362 if (locale.size() > 0) {
Adam Lesinskie119b222014-03-20 18:04:57 -07001363 SourcePos(in->getPrintableSource(), block.getLineNumber()).warning(
1364 "string '%s' marked untranslatable but exists in locale '%s'\n",
1365 String8(name).string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366 locale.string());
1367 // hasErrors = localHasErrors = true;
1368 } else {
1369 // Intentionally empty block:
1370 //
1371 // Don't add untranslatable strings to the localization table; that
1372 // way if we later see localizations of them, they'll be flagged as
1373 // having no default translation.
1374 }
1375 } else {
Adam Lesinskie119b222014-03-20 18:04:57 -07001376 outTable->addLocalization(
1377 name,
1378 locale,
1379 SourcePos(in->getPrintableSource(), block.getLineNumber()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 }
Kenny Root15fe2cb2010-05-28 15:44:32 -07001381
1382 if (formatted == false16) {
1383 curIsFormatted = false;
1384 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 }
1386
1387 curTag = &string16;
1388 curType = string16;
1389 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
1390 curIsStyled = true;
Igor Viarheichyk84410b02014-04-30 11:56:42 -07001391 curIsPseudolocalizable = fileIsTranslatable && (translatable != false16);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001392 } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) {
1393 curTag = &drawable16;
1394 curType = drawable16;
1395 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
1396 } else if (strcmp16(block.getElementName(&len), color16.string()) == 0) {
1397 curTag = &color16;
1398 curType = color16;
1399 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
1400 } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
1401 curTag = &bool16;
1402 curType = bool16;
1403 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
1404 } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
1405 curTag = &integer16;
1406 curType = integer16;
1407 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
1408 } else if (strcmp16(block.getElementName(&len), dimen16.string()) == 0) {
1409 curTag = &dimen16;
1410 curType = dimen16;
1411 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
1412 } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
1413 curTag = &fraction16;
1414 curType = fraction16;
1415 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
1416 } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
1417 curTag = &bag16;
1418 curIsBag = true;
1419 ssize_t attri = block.indexOfAttribute(NULL, "type");
1420 if (attri >= 0) {
1421 curType = String16(block.getAttributeStringValue(attri, &len));
1422 } else {
1423 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1424 "A 'type' attribute is required for <bag>\n");
1425 hasErrors = localHasErrors = true;
1426 }
1427 } else if (strcmp16(block.getElementName(&len), style16.string()) == 0) {
1428 curTag = &style16;
1429 curType = style16;
1430 curIsBag = true;
1431 } else if (strcmp16(block.getElementName(&len), plurals16.string()) == 0) {
1432 curTag = &plurals16;
1433 curType = plurals16;
1434 curIsBag = true;
Anton Krumina2ef5c02014-03-12 14:46:44 -07001435 curIsPseudolocalizable = fileIsTranslatable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001436 } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) {
1437 curTag = &array16;
1438 curType = array16;
1439 curIsBag = true;
Robert Greenwalt1aa81702009-06-05 15:59:15 -07001440 curIsBagReplaceOnOverwrite = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001441 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
1442 if (formatIdx >= 0) {
1443 String16 formatStr = String16(block.getAttributeStringValue(
1444 formatIdx, &len));
1445 curFormat = parse_flags(formatStr.string(), formatStr.size(),
1446 gFormatFlags);
1447 if (curFormat == 0) {
1448 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1449 "Tag <array> 'format' attribute value \"%s\" not valid\n",
1450 String8(formatStr).string());
1451 hasErrors = localHasErrors = true;
1452 }
1453 }
1454 } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
Josh Stone02feeb42011-01-17 18:34:11 -08001455 // Check whether these strings need valid formats.
1456 // (simplified form of what string16 does above)
Anton Krumina2ef5c02014-03-12 14:46:44 -07001457 bool isTranslatable = false;
Josh Stone02feeb42011-01-17 18:34:11 -08001458 size_t n = block.getAttributeCount();
Narayan Kamath7e1b8ff2013-12-10 09:49:59 +00001459
1460 // Pseudolocalizable by default, unless this string array isn't
1461 // translatable.
Josh Stone02feeb42011-01-17 18:34:11 -08001462 for (size_t i = 0; i < n; i++) {
1463 size_t length;
Dan Albertf348c152014-09-08 18:28:00 -07001464 const char16_t* attr = block.getAttributeName(i, &length);
Narayan Kamath7e1b8ff2013-12-10 09:49:59 +00001465 if (strcmp16(attr, formatted16.string()) == 0) {
Dan Albertf348c152014-09-08 18:28:00 -07001466 const char16_t* value = block.getAttributeStringValue(i, &length);
Josh Stone02feeb42011-01-17 18:34:11 -08001467 if (strcmp16(value, false16.string()) == 0) {
1468 curIsFormatted = false;
Josh Stone02feeb42011-01-17 18:34:11 -08001469 }
Anton Krumina2ef5c02014-03-12 14:46:44 -07001470 } else if (strcmp16(attr, translatable16.string()) == 0) {
Dan Albertf348c152014-09-08 18:28:00 -07001471 const char16_t* value = block.getAttributeStringValue(i, &length);
Anton Krumina2ef5c02014-03-12 14:46:44 -07001472 if (strcmp16(value, false16.string()) == 0) {
1473 isTranslatable = false;
1474 }
Josh Stone02feeb42011-01-17 18:34:11 -08001475 }
1476 }
1477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 curTag = &string_array16;
1479 curType = array16;
1480 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
1481 curIsBag = true;
Robert Greenwalt1aa81702009-06-05 15:59:15 -07001482 curIsBagReplaceOnOverwrite = true;
Anton Krumina2ef5c02014-03-12 14:46:44 -07001483 curIsPseudolocalizable = isTranslatable && fileIsTranslatable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
1485 curTag = &integer_array16;
1486 curType = array16;
1487 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
1488 curIsBag = true;
Robert Greenwalt1aa81702009-06-05 15:59:15 -07001489 curIsBagReplaceOnOverwrite = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 } else {
1491 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1492 "Found tag %s where item is expected\n",
1493 String8(block.getElementName(&len)).string());
1494 return UNKNOWN_ERROR;
1495 }
1496
1497 String16 ident;
1498 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
1499 if (identIdx >= 0) {
1500 ident = String16(block.getAttributeStringValue(identIdx, &len));
1501 } else {
1502 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1503 "A 'name' attribute is required for <%s>\n",
1504 String8(*curTag).string());
1505 hasErrors = localHasErrors = true;
1506 }
1507
Dianne Hackborn407f6252010-10-04 11:31:17 -07001508 String16 product;
1509 identIdx = block.indexOfAttribute(NULL, "product");
1510 if (identIdx >= 0) {
1511 product = String16(block.getAttributeStringValue(identIdx, &len));
1512 }
1513
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001514 String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr);
1515
1516 if (curIsBag) {
1517 // Figure out the parent of this bag...
1518 String16 parentIdent;
1519 ssize_t parentIdentIdx = block.indexOfAttribute(NULL, "parent");
1520 if (parentIdentIdx >= 0) {
1521 parentIdent = String16(block.getAttributeStringValue(parentIdentIdx, &len));
1522 } else {
1523 ssize_t sep = ident.findLast('.');
1524 if (sep >= 0) {
1525 parentIdent.setTo(ident, sep);
1526 }
1527 }
1528
1529 if (!localHasErrors) {
Robert Greenwalt1aa81702009-06-05 15:59:15 -07001530 err = outTable->startBag(SourcePos(in->getPrintableSource(),
1531 block.getLineNumber()), myPackage, curType, ident,
1532 parentIdent, &curParams,
1533 overwrite, curIsBagReplaceOnOverwrite);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 if (err != NO_ERROR) {
1535 hasErrors = localHasErrors = true;
1536 }
1537 }
1538
1539 ssize_t elmIndex = 0;
1540 char elmIndexStr[14];
1541 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1542 && code != ResXMLTree::BAD_DOCUMENT) {
1543
1544 if (code == ResXMLTree::START_TAG) {
1545 if (strcmp16(block.getElementName(&len), item16.string()) != 0) {
1546 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1547 "Tag <%s> can not appear inside <%s>, only <item>\n",
1548 String8(block.getElementName(&len)).string(),
1549 String8(*curTag).string());
1550 return UNKNOWN_ERROR;
1551 }
1552
1553 String16 itemIdent;
1554 if (curType == array16) {
1555 sprintf(elmIndexStr, "^index_%d", (int)elmIndex++);
1556 itemIdent = String16(elmIndexStr);
1557 } else if (curType == plurals16) {
1558 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "quantity");
1559 if (itemIdentIdx >= 0) {
1560 String16 quantity16(block.getAttributeStringValue(itemIdentIdx, &len));
1561 if (quantity16 == other16) {
1562 itemIdent = quantityOther16;
1563 }
1564 else if (quantity16 == zero16) {
1565 itemIdent = quantityZero16;
1566 }
1567 else if (quantity16 == one16) {
1568 itemIdent = quantityOne16;
1569 }
1570 else if (quantity16 == two16) {
1571 itemIdent = quantityTwo16;
1572 }
1573 else if (quantity16 == few16) {
1574 itemIdent = quantityFew16;
1575 }
1576 else if (quantity16 == many16) {
1577 itemIdent = quantityMany16;
1578 }
1579 else {
1580 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1581 "Illegal 'quantity' attribute is <item> inside <plurals>\n");
1582 hasErrors = localHasErrors = true;
1583 }
1584 } else {
1585 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1586 "A 'quantity' attribute is required for <item> inside <plurals>\n");
1587 hasErrors = localHasErrors = true;
1588 }
1589 } else {
1590 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
1591 if (itemIdentIdx >= 0) {
1592 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
1593 } else {
1594 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1595 "A 'name' attribute is required for <item>\n");
1596 hasErrors = localHasErrors = true;
1597 }
1598 }
1599
1600 ResXMLParser::ResXMLPosition parserPosition;
1601 block.getPosition(&parserPosition);
1602
1603 err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
Kenny Root15fe2cb2010-05-28 15:44:32 -07001604 ident, parentIdent, itemIdent, curFormat, curIsFormatted,
Anton Krumina2ef5c02014-03-12 14:46:44 -07001605 product, NO_PSEUDOLOCALIZATION, overwrite, outTable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001606 if (err == NO_ERROR) {
1607 if (curIsPseudolocalizable && localeIsDefined(curParams)
Anton Krumina2ef5c02014-03-12 14:46:44 -07001608 && bundle->getPseudolocalize() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 // pseudolocalize here
Anton Krumina2ef5c02014-03-12 14:46:44 -07001610 if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) ==
1611 PSEUDO_ACCENTED) {
1612 block.setPosition(parserPosition);
1613 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
1614 curType, ident, parentIdent, itemIdent, curFormat,
1615 curIsFormatted, product, PSEUDO_ACCENTED,
1616 overwrite, outTable);
1617 }
1618 if ((PSEUDO_BIDI & bundle->getPseudolocalize()) ==
1619 PSEUDO_BIDI) {
1620 block.setPosition(parserPosition);
1621 err = parseAndAddBag(bundle, in, &block, pseudoBidiParams, myPackage,
1622 curType, ident, parentIdent, itemIdent, curFormat,
1623 curIsFormatted, product, PSEUDO_BIDI,
1624 overwrite, outTable);
1625 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001626 }
Anton Krumina2ef5c02014-03-12 14:46:44 -07001627 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001628 if (err != NO_ERROR) {
1629 hasErrors = localHasErrors = true;
1630 }
1631 } else if (code == ResXMLTree::END_TAG) {
1632 if (strcmp16(block.getElementName(&len), curTag->string()) != 0) {
1633 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1634 "Found tag </%s> where </%s> is expected\n",
1635 String8(block.getElementName(&len)).string(),
1636 String8(*curTag).string());
1637 return UNKNOWN_ERROR;
1638 }
1639 break;
1640 }
1641 }
1642 } else {
1643 ResXMLParser::ResXMLPosition parserPosition;
1644 block.getPosition(&parserPosition);
1645
1646 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
Kenny Root15fe2cb2010-05-28 15:44:32 -07001647 *curTag, curIsStyled, curFormat, curIsFormatted,
Anton Krumina2ef5c02014-03-12 14:46:44 -07001648 product, NO_PSEUDOLOCALIZATION, overwrite, &skippedResourceNames, outTable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001649
1650 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
1651 hasErrors = localHasErrors = true;
1652 }
1653 else if (err == NO_ERROR) {
1654 if (curIsPseudolocalizable && localeIsDefined(curParams)
Anton Krumina2ef5c02014-03-12 14:46:44 -07001655 && bundle->getPseudolocalize() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001656 // pseudolocalize here
Anton Krumina2ef5c02014-03-12 14:46:44 -07001657 if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) ==
1658 PSEUDO_ACCENTED) {
1659 block.setPosition(parserPosition);
1660 err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
1661 ident, *curTag, curIsStyled, curFormat,
1662 curIsFormatted, product,
1663 PSEUDO_ACCENTED, overwrite, &skippedResourceNames, outTable);
1664 }
1665 if ((PSEUDO_BIDI & bundle->getPseudolocalize()) ==
1666 PSEUDO_BIDI) {
1667 block.setPosition(parserPosition);
1668 err = parseAndAddEntry(bundle, in, &block, pseudoBidiParams,
1669 myPackage, curType, ident, *curTag, curIsStyled, curFormat,
1670 curIsFormatted, product,
1671 PSEUDO_BIDI, overwrite, &skippedResourceNames, outTable);
1672 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 if (err != NO_ERROR) {
1674 hasErrors = localHasErrors = true;
1675 }
1676 }
1677 }
1678 }
1679
1680#if 0
1681 if (comment.size() > 0) {
1682 printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).string(),
1683 String8(curType).string(), String8(ident).string(),
1684 String8(comment).string());
1685 }
1686#endif
1687 if (!localHasErrors) {
1688 outTable->appendComment(myPackage, curType, ident, comment, false);
1689 }
1690 }
1691 else if (code == ResXMLTree::END_TAG) {
1692 if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
1693 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1694 "Unexpected end tag %s\n", String8(block.getElementName(&len)).string());
1695 return UNKNOWN_ERROR;
1696 }
1697 }
1698 else if (code == ResXMLTree::START_NAMESPACE || code == ResXMLTree::END_NAMESPACE) {
1699 }
1700 else if (code == ResXMLTree::TEXT) {
1701 if (isWhitespace(block.getText(&len))) {
1702 continue;
1703 }
1704 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
1705 "Found text \"%s\" where item tag is expected\n",
1706 String8(block.getText(&len)).string());
1707 return UNKNOWN_ERROR;
1708 }
1709 }
1710
Adam Lesinski2b8e82f2013-10-04 12:06:38 -07001711 // For every resource defined, there must be exist one variant with a product attribute
1712 // set to 'default' (or no product attribute at all).
1713 // We check to see that for every resource that was ignored because of a mismatched
1714 // product attribute, some product variant of that resource was processed.
1715 for (size_t i = 0; i < skippedResourceNames.size(); i++) {
1716 if (skippedResourceNames[i]) {
1717 const type_ident_pair_t& p = skippedResourceNames.keyAt(i);
1718 if (!outTable->hasBagOrEntry(myPackage, p.type, p.ident)) {
1719 const char* bundleProduct =
1720 (bundle->getProduct() == NULL) ? "" : bundle->getProduct();
1721 fprintf(stderr, "In resource file %s: %s\n",
1722 in->getPrintableSource().string(),
1723 curParams.toString().string());
1724
1725 fprintf(stderr, "\t%s '%s' does not match product %s.\n"
1726 "\tYou may have forgotten to include a 'default' product variant"
1727 " of the resource.\n",
1728 String8(p.type).string(), String8(p.ident).string(),
1729 bundleProduct[0] == 0 ? "default" : bundleProduct);
1730 return UNKNOWN_ERROR;
1731 }
1732 }
1733 }
1734
Andreas Gampe2412f842014-09-30 20:55:57 -07001735 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001736}
1737
1738ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage)
1739 : mAssetsPackage(assetsPackage), mNextPackageId(1), mHaveAppPackage(false),
1740 mIsAppPackage(!bundle->getExtending()),
1741 mNumLocal(0),
1742 mBundle(bundle)
1743{
1744}
1745
1746status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets)
1747{
1748 status_t err = assets->buildIncludedResources(bundle);
1749 if (err != NO_ERROR) {
1750 return err;
1751 }
1752
1753 // For future reference to included resources.
1754 mAssets = assets;
1755
1756 const ResTable& incl = assets->getIncludedResources();
1757
1758 // Retrieve all the packages.
1759 const size_t N = incl.getBasePackageCount();
1760 for (size_t phase=0; phase<2; phase++) {
1761 for (size_t i=0; i<N; i++) {
1762 String16 name(incl.getBasePackageName(i));
1763 uint32_t id = incl.getBasePackageId(i);
1764 // First time through: only add base packages (id
1765 // is not 0); second time through add the other
1766 // packages.
1767 if (phase != 0) {
1768 if (id != 0) {
1769 // Skip base packages -- already one.
1770 id = 0;
1771 } else {
1772 // Assign a dynamic id.
1773 id = mNextPackageId;
1774 }
1775 } else if (id != 0) {
1776 if (id == 127) {
1777 if (mHaveAppPackage) {
Dianne Hackborna96cbb42009-05-13 15:06:13 -07001778 fprintf(stderr, "Included resources have two application packages!\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001779 return UNKNOWN_ERROR;
1780 }
1781 mHaveAppPackage = true;
1782 }
1783 if (mNextPackageId > id) {
1784 fprintf(stderr, "Included base package ID %d already in use!\n", id);
1785 return UNKNOWN_ERROR;
1786 }
1787 }
1788 if (id != 0) {
Andreas Gampe2412f842014-09-30 20:55:57 -07001789 if (kIsDebug) {
1790 printf("Including package %s with ID=%d\n", String8(name).string(), id);
1791 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 sp<Package> p = new Package(name, id);
1793 mPackages.add(name, p);
1794 mOrderedPackages.add(p);
1795
1796 if (id >= mNextPackageId) {
1797 mNextPackageId = id+1;
1798 }
1799 }
1800 }
1801 }
1802
1803 // Every resource table always has one first entry, the bag attributes.
1804 const SourcePos unknown(String8("????"), 0);
1805 sp<Type> attr = getType(mAssetsPackage, String16("attr"), unknown);
1806
1807 return NO_ERROR;
1808}
1809
1810status_t ResourceTable::addPublic(const SourcePos& sourcePos,
1811 const String16& package,
1812 const String16& type,
1813 const String16& name,
1814 const uint32_t ident)
1815{
1816 uint32_t rid = mAssets->getIncludedResources()
1817 .identifierForName(name.string(), name.size(),
1818 type.string(), type.size(),
1819 package.string(), package.size());
1820 if (rid != 0) {
1821 sourcePos.error("Error declaring public resource %s/%s for included package %s\n",
1822 String8(type).string(), String8(name).string(),
1823 String8(package).string());
1824 return UNKNOWN_ERROR;
1825 }
1826
1827 sp<Type> t = getType(package, type, sourcePos);
1828 if (t == NULL) {
1829 return UNKNOWN_ERROR;
1830 }
1831 return t->addPublic(sourcePos, name, ident);
1832}
1833
1834status_t ResourceTable::addEntry(const SourcePos& sourcePos,
1835 const String16& package,
1836 const String16& type,
1837 const String16& name,
1838 const String16& value,
1839 const Vector<StringPool::entry_style_span>* style,
1840 const ResTable_config* params,
1841 const bool doSetIndex,
1842 const int32_t format,
1843 const bool overwrite)
1844{
1845 // Check for adding entries in other packages... for now we do
1846 // nothing. We need to do the right thing here to support skinning.
1847 uint32_t rid = mAssets->getIncludedResources()
1848 .identifierForName(name.string(), name.size(),
1849 type.string(), type.size(),
1850 package.string(), package.size());
1851 if (rid != 0) {
1852 return NO_ERROR;
1853 }
1854
1855#if 0
1856 if (name == String16("left")) {
1857 printf("Adding entry left: file=%s, line=%d, type=%s, value=%s\n",
1858 sourcePos.file.string(), sourcePos.line, String8(type).string(),
1859 String8(value).string());
1860 }
1861#endif
Robert Greenwaltf878e2d2009-06-09 09:14:20 -07001862
1863 sp<Entry> e = getEntry(package, type, name, sourcePos, overwrite,
1864 params, doSetIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001865 if (e == NULL) {
1866 return UNKNOWN_ERROR;
1867 }
1868 status_t err = e->setItem(sourcePos, value, style, format, overwrite);
1869 if (err == NO_ERROR) {
1870 mNumLocal++;
1871 }
1872 return err;
1873}
1874
1875status_t ResourceTable::startBag(const SourcePos& sourcePos,
1876 const String16& package,
1877 const String16& type,
1878 const String16& name,
1879 const String16& bagParent,
1880 const ResTable_config* params,
Robert Greenwalt1aa81702009-06-05 15:59:15 -07001881 bool overlay,
Andreas Gampe2412f842014-09-30 20:55:57 -07001882 bool replace, bool /* isId */)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883{
Robert Greenwalt4b4f4a92009-04-02 16:55:50 -07001884 status_t result = NO_ERROR;
1885
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 // Check for adding entries in other packages... for now we do
1887 // nothing. We need to do the right thing here to support skinning.
1888 uint32_t rid = mAssets->getIncludedResources()
1889 .identifierForName(name.string(), name.size(),
1890 type.string(), type.size(),
1891 package.string(), package.size());
1892 if (rid != 0) {
1893 return NO_ERROR;
1894 }
1895
1896#if 0
1897 if (name == String16("left")) {
1898 printf("Adding bag left: file=%s, line=%d, type=%s\n",
1899 sourcePos.file.striing(), sourcePos.line, String8(type).string());
1900 }
1901#endif
Xavier Ducrohet99080c72010-02-04 18:45:31 -08001902 if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) {
Dianne Hackborn58c27a02009-08-13 13:36:00 -07001903 bool canAdd = false;
1904 sp<Package> p = mPackages.valueFor(package);
1905 if (p != NULL) {
1906 sp<Type> t = p->getTypes().valueFor(type);
1907 if (t != NULL) {
1908 if (t->getCanAddEntries().indexOf(name) >= 0) {
1909 canAdd = true;
1910 }
1911 }
1912 }
1913 if (!canAdd) {
1914 sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n",
1915 String8(name).string());
1916 return UNKNOWN_ERROR;
1917 }
Robert Greenwalt1aa81702009-06-05 15:59:15 -07001918 }
Robert Greenwaltf878e2d2009-06-09 09:14:20 -07001919 sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001920 if (e == NULL) {
1921 return UNKNOWN_ERROR;
1922 }
1923
1924 // If a parent is explicitly specified, set it.
1925 if (bagParent.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001926 e->setParent(bagParent);
1927 }
Robert Greenwalt4b4f4a92009-04-02 16:55:50 -07001928
1929 if ((result = e->makeItABag(sourcePos)) != NO_ERROR) {
1930 return result;
1931 }
Robert Greenwalt9411a392009-04-03 16:44:30 -07001932
Robert Greenwalt1aa81702009-06-05 15:59:15 -07001933 if (overlay && replace) {
Robert Greenwalt9411a392009-04-03 16:44:30 -07001934 return e->emptyBag(sourcePos);
1935 }
1936 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001937}
1938
1939status_t ResourceTable::addBag(const SourcePos& sourcePos,
1940 const String16& package,
1941 const String16& type,
1942 const String16& name,
1943 const String16& bagParent,
1944 const String16& bagKey,
1945 const String16& value,
1946 const Vector<StringPool::entry_style_span>* style,
1947 const ResTable_config* params,
1948 bool replace, bool isId, const int32_t format)
1949{
1950 // Check for adding entries in other packages... for now we do
1951 // nothing. We need to do the right thing here to support skinning.
1952 uint32_t rid = mAssets->getIncludedResources()
1953 .identifierForName(name.string(), name.size(),
1954 type.string(), type.size(),
1955 package.string(), package.size());
1956 if (rid != 0) {
1957 return NO_ERROR;
1958 }
1959
1960#if 0
1961 if (name == String16("left")) {
1962 printf("Adding bag left: file=%s, line=%d, type=%s\n",
1963 sourcePos.file.striing(), sourcePos.line, String8(type).string());
1964 }
1965#endif
Robert Greenwaltf878e2d2009-06-09 09:14:20 -07001966 sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001967 if (e == NULL) {
1968 return UNKNOWN_ERROR;
1969 }
1970
1971 // If a parent is explicitly specified, set it.
1972 if (bagParent.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 e->setParent(bagParent);
1974 }
1975
1976 const bool first = e->getBag().indexOfKey(bagKey) < 0;
1977 status_t err = e->addToBag(sourcePos, bagKey, value, style, replace, isId, format);
1978 if (err == NO_ERROR && first) {
1979 mNumLocal++;
1980 }
1981 return err;
1982}
1983
1984bool ResourceTable::hasBagOrEntry(const String16& package,
1985 const String16& type,
1986 const String16& name) const
1987{
1988 // First look for this in the included resources...
1989 uint32_t rid = mAssets->getIncludedResources()
1990 .identifierForName(name.string(), name.size(),
1991 type.string(), type.size(),
1992 package.string(), package.size());
1993 if (rid != 0) {
1994 return true;
1995 }
1996
1997 sp<Package> p = mPackages.valueFor(package);
1998 if (p != NULL) {
1999 sp<Type> t = p->getTypes().valueFor(type);
2000 if (t != NULL) {
2001 sp<ConfigList> c = t->getConfigs().valueFor(name);
2002 if (c != NULL) return true;
2003 }
2004 }
2005
2006 return false;
2007}
2008
Eric Fischer914f7e62011-09-27 16:09:41 -07002009bool ResourceTable::hasBagOrEntry(const String16& package,
2010 const String16& type,
2011 const String16& name,
2012 const ResTable_config& config) const
2013{
2014 // First look for this in the included resources...
2015 uint32_t rid = mAssets->getIncludedResources()
2016 .identifierForName(name.string(), name.size(),
2017 type.string(), type.size(),
2018 package.string(), package.size());
2019 if (rid != 0) {
2020 return true;
2021 }
2022
2023 sp<Package> p = mPackages.valueFor(package);
2024 if (p != NULL) {
2025 sp<Type> t = p->getTypes().valueFor(type);
2026 if (t != NULL) {
2027 sp<ConfigList> c = t->getConfigs().valueFor(name);
2028 if (c != NULL) {
2029 sp<Entry> e = c->getEntries().valueFor(config);
2030 if (e != NULL) {
2031 return true;
2032 }
2033 }
2034 }
2035 }
2036
2037 return false;
2038}
2039
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002040bool ResourceTable::hasBagOrEntry(const String16& ref,
2041 const String16* defType,
2042 const String16* defPackage)
2043{
2044 String16 package, type, name;
2045 if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
2046 defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
2047 return false;
2048 }
2049 return hasBagOrEntry(package, type, name);
2050}
2051
2052bool ResourceTable::appendComment(const String16& package,
2053 const String16& type,
2054 const String16& name,
2055 const String16& comment,
2056 bool onlyIfEmpty)
2057{
2058 if (comment.size() <= 0) {
2059 return true;
2060 }
2061
2062 sp<Package> p = mPackages.valueFor(package);
2063 if (p != NULL) {
2064 sp<Type> t = p->getTypes().valueFor(type);
2065 if (t != NULL) {
2066 sp<ConfigList> c = t->getConfigs().valueFor(name);
2067 if (c != NULL) {
2068 c->appendComment(comment, onlyIfEmpty);
2069 return true;
2070 }
2071 }
2072 }
2073 return false;
2074}
2075
2076bool ResourceTable::appendTypeComment(const String16& package,
2077 const String16& type,
2078 const String16& name,
2079 const String16& comment)
2080{
2081 if (comment.size() <= 0) {
2082 return true;
2083 }
2084
2085 sp<Package> p = mPackages.valueFor(package);
2086 if (p != NULL) {
2087 sp<Type> t = p->getTypes().valueFor(type);
2088 if (t != NULL) {
2089 sp<ConfigList> c = t->getConfigs().valueFor(name);
2090 if (c != NULL) {
2091 c->appendTypeComment(comment);
2092 return true;
2093 }
2094 }
2095 }
2096 return false;
2097}
2098
Dianne Hackborn58c27a02009-08-13 13:36:00 -07002099void ResourceTable::canAddEntry(const SourcePos& pos,
2100 const String16& package, const String16& type, const String16& name)
2101{
2102 sp<Type> t = getType(package, type, pos);
2103 if (t != NULL) {
2104 t->canAddEntry(name);
2105 }
2106}
2107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002108size_t ResourceTable::size() const {
2109 return mPackages.size();
2110}
2111
2112size_t ResourceTable::numLocalResources() const {
2113 return mNumLocal;
2114}
2115
2116bool ResourceTable::hasResources() const {
2117 return mNumLocal > 0;
2118}
2119
2120sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
2121{
2122 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
2123 status_t err = flatten(bundle, data);
2124 return err == NO_ERROR ? data : NULL;
2125}
2126
2127inline uint32_t ResourceTable::getResId(const sp<Package>& p,
2128 const sp<Type>& t,
2129 uint32_t nameId)
2130{
2131 return makeResId(p->getAssignedId(), t->getIndex(), nameId);
2132}
2133
2134uint32_t ResourceTable::getResId(const String16& package,
2135 const String16& type,
2136 const String16& name,
2137 bool onlyPublic) const
2138{
Christopher Tated8dde13a2012-11-16 15:58:08 -08002139 uint32_t id = ResourceIdCache::lookup(package, type, name, onlyPublic);
2140 if (id != 0) return id; // cache hit
2141
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002142 sp<Package> p = mPackages.valueFor(package);
2143 if (p == NULL) return 0;
2144
2145 // First look for this in the included resources...
2146 uint32_t specFlags = 0;
2147 uint32_t rid = mAssets->getIncludedResources()
2148 .identifierForName(name.string(), name.size(),
2149 type.string(), type.size(),
2150 package.string(), package.size(),
2151 &specFlags);
2152 if (rid != 0) {
2153 if (onlyPublic) {
2154 if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
2155 return 0;
2156 }
2157 }
2158
2159 if (Res_INTERNALID(rid)) {
Christopher Tated8dde13a2012-11-16 15:58:08 -08002160 return ResourceIdCache::store(package, type, name, onlyPublic, rid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002161 }
Christopher Tated8dde13a2012-11-16 15:58:08 -08002162 return ResourceIdCache::store(package, type, name, onlyPublic,
2163 Res_MAKEID(p->getAssignedId()-1, Res_GETTYPE(rid), Res_GETENTRY(rid)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002164 }
2165
2166 sp<Type> t = p->getTypes().valueFor(type);
2167 if (t == NULL) return 0;
2168 sp<ConfigList> c = t->getConfigs().valueFor(name);
2169 if (c == NULL) return 0;
2170 int32_t ei = c->getEntryIndex();
2171 if (ei < 0) return 0;
Christopher Tated8dde13a2012-11-16 15:58:08 -08002172
2173 return ResourceIdCache::store(package, type, name, onlyPublic,
2174 getResId(p, t, ei));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002175}
2176
2177uint32_t ResourceTable::getResId(const String16& ref,
2178 const String16* defType,
2179 const String16* defPackage,
2180 const char** outErrorMsg,
2181 bool onlyPublic) const
2182{
2183 String16 package, type, name;
Dianne Hackborn426431a2011-06-09 11:29:08 -07002184 bool refOnlyPublic = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002185 if (!ResTable::expandResourceRef(
2186 ref.string(), ref.size(), &package, &type, &name,
2187 defType, defPackage ? defPackage:&mAssetsPackage,
Dianne Hackborn426431a2011-06-09 11:29:08 -07002188 outErrorMsg, &refOnlyPublic)) {
Andreas Gampe2412f842014-09-30 20:55:57 -07002189 if (kIsDebug) {
2190 printf("Expanding resource: ref=%s\n", String8(ref).string());
2191 printf("Expanding resource: defType=%s\n",
2192 defType ? String8(*defType).string() : "NULL");
2193 printf("Expanding resource: defPackage=%s\n",
2194 defPackage ? String8(*defPackage).string() : "NULL");
2195 printf("Expanding resource: ref=%s\n", String8(ref).string());
2196 printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
2197 String8(package).string(), String8(type).string(),
2198 String8(name).string());
2199 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002200 return 0;
2201 }
Dianne Hackborn426431a2011-06-09 11:29:08 -07002202 uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic);
Andreas Gampe2412f842014-09-30 20:55:57 -07002203 if (kIsDebug) {
2204 printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
2205 String8(package).string(), String8(type).string(),
2206 String8(name).string(), res);
2207 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002208 if (res == 0) {
2209 if (outErrorMsg)
2210 *outErrorMsg = "No resource found that matches the given name";
2211 }
2212 return res;
2213}
2214
2215bool ResourceTable::isValidResourceName(const String16& s)
2216{
2217 const char16_t* p = s.string();
2218 bool first = true;
2219 while (*p) {
2220 if ((*p >= 'a' && *p <= 'z')
2221 || (*p >= 'A' && *p <= 'Z')
2222 || *p == '_'
2223 || (!first && *p >= '0' && *p <= '9')) {
2224 first = false;
2225 p++;
2226 continue;
2227 }
2228 return false;
2229 }
2230 return true;
2231}
2232
2233bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
2234 const String16& str,
2235 bool preserveSpaces, bool coerceType,
2236 uint32_t attrID,
2237 const Vector<StringPool::entry_style_span>* style,
2238 String16* outStr, void* accessorCookie,
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002239 uint32_t attrType, const String8* configTypeName,
2240 const ConfigDescription* config)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002241{
2242 String16 finalStr;
2243
2244 bool res = true;
2245 if (style == NULL || style->size() == 0) {
2246 // Text is not styled so it can be any type... let's figure it out.
2247 res = mAssets->getIncludedResources()
2248 .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
2249 coerceType, attrID, NULL, &mAssetsPackage, this,
2250 accessorCookie, attrType);
2251 } else {
2252 // Styled text can only be a string, and while collecting the style
2253 // information we have already processed that string!
2254 outValue->size = sizeof(Res_value);
2255 outValue->res0 = 0;
Kenny Root77456222010-03-10 11:38:05 -08002256 outValue->dataType = outValue->TYPE_STRING;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002257 outValue->data = 0;
2258 finalStr = str;
2259 }
2260
2261 if (!res) {
2262 return false;
2263 }
2264
Kenny Root77456222010-03-10 11:38:05 -08002265 if (outValue->dataType == outValue->TYPE_STRING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002266 // Should do better merging styles.
2267 if (pool) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002268 String8 configStr;
2269 if (config != NULL) {
2270 configStr = config->toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002271 } else {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002272 configStr = "(null)";
2273 }
Andreas Gampe2412f842014-09-30 20:55:57 -07002274 if (kIsDebug) {
2275 printf("Adding to pool string style #%zu config %s: %s\n",
2276 style != NULL ? style->size() : 0U,
2277 configStr.string(), String8(finalStr).string());
2278 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002279 if (style != NULL && style->size() > 0) {
2280 outValue->data = pool->add(finalStr, *style, configTypeName, config);
2281 } else {
2282 outValue->data = pool->add(finalStr, true, configTypeName, config);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002283 }
2284 } else {
2285 // Caller will fill this in later.
2286 outValue->data = 0;
2287 }
2288
2289 if (outStr) {
2290 *outStr = finalStr;
2291 }
2292
2293 }
2294
2295 return true;
2296}
2297
2298uint32_t ResourceTable::getCustomResource(
2299 const String16& package, const String16& type, const String16& name) const
2300{
2301 //printf("getCustomResource: %s %s %s\n", String8(package).string(),
2302 // String8(type).string(), String8(name).string());
2303 sp<Package> p = mPackages.valueFor(package);
2304 if (p == NULL) return 0;
2305 sp<Type> t = p->getTypes().valueFor(type);
2306 if (t == NULL) return 0;
2307 sp<ConfigList> c = t->getConfigs().valueFor(name);
2308 if (c == NULL) return 0;
2309 int32_t ei = c->getEntryIndex();
2310 if (ei < 0) return 0;
2311 return getResId(p, t, ei);
2312}
2313
2314uint32_t ResourceTable::getCustomResourceWithCreation(
2315 const String16& package, const String16& type, const String16& name,
2316 const bool createIfNotFound)
2317{
2318 uint32_t resId = getCustomResource(package, type, name);
2319 if (resId != 0 || !createIfNotFound) {
2320 return resId;
2321 }
2322 String16 value("false");
2323
2324 status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
2325 if (status == NO_ERROR) {
2326 resId = getResId(package, type, name);
2327 return resId;
2328 }
2329 return 0;
2330}
2331
2332uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
2333{
2334 return origPackage;
2335}
2336
2337bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
2338{
2339 //printf("getAttributeType #%08x\n", attrID);
2340 Res_value value;
2341 if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
2342 //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
2343 // String8(getEntry(attrID)->getName()).string(), value.data);
2344 *outType = value.data;
2345 return true;
2346 }
2347 return false;
2348}
2349
2350bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
2351{
2352 //printf("getAttributeMin #%08x\n", attrID);
2353 Res_value value;
2354 if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
2355 *outMin = value.data;
2356 return true;
2357 }
2358 return false;
2359}
2360
2361bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
2362{
2363 //printf("getAttributeMax #%08x\n", attrID);
2364 Res_value value;
2365 if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
2366 *outMax = value.data;
2367 return true;
2368 }
2369 return false;
2370}
2371
2372uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
2373{
2374 //printf("getAttributeL10N #%08x\n", attrID);
2375 Res_value value;
2376 if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
2377 return value.data;
2378 }
2379 return ResTable_map::L10N_NOT_REQUIRED;
2380}
2381
2382bool ResourceTable::getLocalizationSetting()
2383{
2384 return mBundle->getRequireLocalization();
2385}
2386
2387void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
2388{
2389 if (accessorCookie != NULL && fmt != NULL) {
2390 AccessorCookie* ac = (AccessorCookie*)accessorCookie;
2391 int retval=0;
2392 char buf[1024];
2393 va_list ap;
2394 va_start(ap, fmt);
2395 retval = vsnprintf(buf, sizeof(buf), fmt, ap);
2396 va_end(ap);
2397 ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
2398 buf, ac->attr.string(), ac->value.string());
2399 }
2400}
2401
2402bool ResourceTable::getAttributeKeys(
2403 uint32_t attrID, Vector<String16>* outKeys)
2404{
2405 sp<const Entry> e = getEntry(attrID);
2406 if (e != NULL) {
2407 const size_t N = e->getBag().size();
2408 for (size_t i=0; i<N; i++) {
2409 const String16& key = e->getBag().keyAt(i);
2410 if (key.size() > 0 && key.string()[0] != '^') {
2411 outKeys->add(key);
2412 }
2413 }
2414 return true;
2415 }
2416 return false;
2417}
2418
2419bool ResourceTable::getAttributeEnum(
2420 uint32_t attrID, const char16_t* name, size_t nameLen,
2421 Res_value* outValue)
2422{
2423 //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
2424 String16 nameStr(name, nameLen);
2425 sp<const Entry> e = getEntry(attrID);
2426 if (e != NULL) {
2427 const size_t N = e->getBag().size();
2428 for (size_t i=0; i<N; i++) {
2429 //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
2430 // String8(e->getBag().keyAt(i)).string());
2431 if (e->getBag().keyAt(i) == nameStr) {
2432 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
2433 }
2434 }
2435 }
2436 return false;
2437}
2438
2439bool ResourceTable::getAttributeFlags(
2440 uint32_t attrID, const char16_t* name, size_t nameLen,
2441 Res_value* outValue)
2442{
2443 outValue->dataType = Res_value::TYPE_INT_HEX;
2444 outValue->data = 0;
2445
2446 //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
2447 String16 nameStr(name, nameLen);
2448 sp<const Entry> e = getEntry(attrID);
2449 if (e != NULL) {
2450 const size_t N = e->getBag().size();
2451
2452 const char16_t* end = name + nameLen;
2453 const char16_t* pos = name;
Ben Gruver59eb5fd2012-03-06 19:26:22 -08002454 while (pos < end) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002455 const char16_t* start = pos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002456 while (pos < end && *pos != '|') {
2457 pos++;
2458 }
2459
2460 String16 nameStr(start, pos-start);
2461 size_t i;
2462 for (i=0; i<N; i++) {
2463 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
2464 // String8(e->getBag().keyAt(i)).string());
2465 if (e->getBag().keyAt(i) == nameStr) {
2466 Res_value val;
2467 bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
2468 if (!got) {
2469 return false;
2470 }
2471 //printf("Got value: 0x%08x\n", val.data);
2472 outValue->data |= val.data;
2473 break;
2474 }
2475 }
2476
2477 if (i >= N) {
2478 // Didn't find this flag identifier.
2479 return false;
2480 }
Ben Gruver59eb5fd2012-03-06 19:26:22 -08002481 pos++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002482 }
2483
2484 return true;
2485 }
2486 return false;
2487}
2488
2489status_t ResourceTable::assignResourceIds()
2490{
2491 const size_t N = mOrderedPackages.size();
2492 size_t pi;
2493 status_t firstError = NO_ERROR;
2494
2495 // First generate all bag attributes and assign indices.
2496 for (pi=0; pi<N; pi++) {
2497 sp<Package> p = mOrderedPackages.itemAt(pi);
2498 if (p == NULL || p->getTypes().size() == 0) {
2499 // Empty, skip!
2500 continue;
2501 }
2502
2503 status_t err = p->applyPublicTypeOrder();
2504 if (err != NO_ERROR && firstError == NO_ERROR) {
2505 firstError = err;
2506 }
2507
2508 // Generate attributes...
2509 const size_t N = p->getOrderedTypes().size();
2510 size_t ti;
2511 for (ti=0; ti<N; ti++) {
2512 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2513 if (t == NULL) {
2514 continue;
2515 }
2516 const size_t N = t->getOrderedConfigs().size();
2517 for (size_t ci=0; ci<N; ci++) {
2518 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2519 if (c == NULL) {
2520 continue;
2521 }
2522 const size_t N = c->getEntries().size();
2523 for (size_t ei=0; ei<N; ei++) {
2524 sp<Entry> e = c->getEntries().valueAt(ei);
2525 if (e == NULL) {
2526 continue;
2527 }
2528 status_t err = e->generateAttributes(this, p->getName());
2529 if (err != NO_ERROR && firstError == NO_ERROR) {
2530 firstError = err;
2531 }
2532 }
2533 }
2534 }
2535
2536 const SourcePos unknown(String8("????"), 0);
2537 sp<Type> attr = p->getType(String16("attr"), unknown);
2538
2539 // Assign indices...
2540 for (ti=0; ti<N; ti++) {
2541 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2542 if (t == NULL) {
2543 continue;
2544 }
2545 err = t->applyPublicEntryOrder();
2546 if (err != NO_ERROR && firstError == NO_ERROR) {
2547 firstError = err;
2548 }
2549
2550 const size_t N = t->getOrderedConfigs().size();
2551 t->setIndex(ti+1);
2552
2553 LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
2554 "First type is not attr!");
2555
2556 for (size_t ei=0; ei<N; ei++) {
2557 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
2558 if (c == NULL) {
2559 continue;
2560 }
2561 c->setEntryIndex(ei);
2562 }
2563 }
2564
2565 // Assign resource IDs to keys in bags...
2566 for (ti=0; ti<N; ti++) {
2567 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2568 if (t == NULL) {
2569 continue;
2570 }
2571 const size_t N = t->getOrderedConfigs().size();
2572 for (size_t ci=0; ci<N; ci++) {
2573 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2574 //printf("Ordered config #%d: %p\n", ci, c.get());
2575 const size_t N = c->getEntries().size();
2576 for (size_t ei=0; ei<N; ei++) {
2577 sp<Entry> e = c->getEntries().valueAt(ei);
2578 if (e == NULL) {
2579 continue;
2580 }
2581 status_t err = e->assignResourceIds(this, p->getName());
2582 if (err != NO_ERROR && firstError == NO_ERROR) {
2583 firstError = err;
2584 }
2585 }
2586 }
2587 }
2588 }
2589 return firstError;
2590}
2591
2592status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
2593 const size_t N = mOrderedPackages.size();
2594 size_t pi;
2595
2596 for (pi=0; pi<N; pi++) {
2597 sp<Package> p = mOrderedPackages.itemAt(pi);
2598 if (p->getTypes().size() == 0) {
2599 // Empty, skip!
2600 continue;
2601 }
2602
2603 const size_t N = p->getOrderedTypes().size();
2604 size_t ti;
2605
2606 for (ti=0; ti<N; ti++) {
2607 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2608 if (t == NULL) {
2609 continue;
2610 }
2611 const size_t N = t->getOrderedConfigs().size();
2612 sp<AaptSymbols> typeSymbols;
2613 typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
2614 for (size_t ci=0; ci<N; ci++) {
2615 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2616 if (c == NULL) {
2617 continue;
2618 }
2619 uint32_t rid = getResId(p, t, ci);
2620 if (rid == 0) {
2621 return UNKNOWN_ERROR;
2622 }
2623 if (Res_GETPACKAGE(rid) == (size_t)(p->getAssignedId()-1)) {
2624 typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
2625
2626 String16 comment(c->getComment());
2627 typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
Adam Lesinski2b8e82f2013-10-04 12:06:38 -07002628 //printf("Type symbol [%08x] %s comment: %s\n", rid,
2629 // String8(c->getName()).string(), String8(comment).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002630 comment = c->getTypeComment();
2631 typeSymbols->appendTypeComment(String8(c->getName()), comment);
2632 } else {
2633#if 0
2634 printf("**** NO MATCH: 0x%08x vs 0x%08x\n",
2635 Res_GETPACKAGE(rid), p->getAssignedId());
2636#endif
2637 }
2638 }
2639 }
2640 }
2641 return NO_ERROR;
2642}
2643
2644
2645void
Adam Lesinskie119b222014-03-20 18:04:57 -07002646ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002647{
Adam Lesinskie119b222014-03-20 18:04:57 -07002648 mLocalizations[name][locale] = src;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002649}
2650
2651
2652/*!
2653 * Flag various sorts of localization problems. '+' indicates checks already implemented;
2654 * '-' indicates checks that will be implemented in the future.
2655 *
2656 * + A localized string for which no default-locale version exists => warning
2657 * + A string for which no version in an explicitly-requested locale exists => warning
2658 * + A localized translation of an translateable="false" string => warning
2659 * - A localized string not provided in every locale used by the table
2660 */
2661status_t
2662ResourceTable::validateLocalizations(void)
2663{
2664 status_t err = NO_ERROR;
2665 const String8 defaultLocale;
2666
2667 // For all strings...
Adam Lesinskie119b222014-03-20 18:04:57 -07002668 for (map<String16, map<String8, SourcePos> >::iterator nameIter = mLocalizations.begin();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002669 nameIter != mLocalizations.end();
2670 nameIter++) {
Adam Lesinskie119b222014-03-20 18:04:57 -07002671 const map<String8, SourcePos>& configSrcMap = nameIter->second;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002672
2673 // Look for strings with no default localization
Adam Lesinskie119b222014-03-20 18:04:57 -07002674 if (configSrcMap.count(defaultLocale) == 0) {
2675 SourcePos().warning("string '%s' has no default translation.",
2676 String8(nameIter->first).string());
2677 if (mBundle->getVerbose()) {
2678 for (map<String8, SourcePos>::const_iterator locales = configSrcMap.begin();
2679 locales != configSrcMap.end();
2680 locales++) {
2681 locales->second.printf("locale %s found", locales->first.string());
2682 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002683 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002684 // !!! TODO: throw an error here in some circumstances
2685 }
2686
2687 // Check that all requested localizations are present for this string
2688 if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
2689 const char* allConfigs = mBundle->getConfigurations();
2690 const char* start = allConfigs;
2691 const char* comma;
2692
Adam Lesinskie119b222014-03-20 18:04:57 -07002693 set<String8> missingConfigs;
2694 AaptLocaleValue locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002695 do {
2696 String8 config;
2697 comma = strchr(start, ',');
2698 if (comma != NULL) {
2699 config.setTo(start, comma - start);
2700 start = comma + 1;
2701 } else {
2702 config.setTo(start);
2703 }
2704
Adam Lesinskie119b222014-03-20 18:04:57 -07002705 if (!locale.initFromFilterString(config)) {
2706 continue;
2707 }
2708
Anton Krumina2ef5c02014-03-12 14:46:44 -07002709 // don't bother with the pseudolocale "en_XA" or "ar_XB"
2710 if (config != "en_XA" && config != "ar_XB") {
Adam Lesinskie119b222014-03-20 18:04:57 -07002711 if (configSrcMap.find(config) == configSrcMap.end()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002712 // okay, no specific localization found. it's possible that we are
2713 // requiring a specific regional localization [e.g. de_DE] but there is an
2714 // available string in the generic language localization [e.g. de];
2715 // consider that string to have fulfilled the localization requirement.
2716 String8 region(config.string(), 2);
Adam Lesinskie119b222014-03-20 18:04:57 -07002717 if (configSrcMap.find(region) == configSrcMap.end() &&
2718 configSrcMap.count(defaultLocale) == 0) {
2719 missingConfigs.insert(config);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002720 }
2721 }
2722 }
Adam Lesinskie119b222014-03-20 18:04:57 -07002723 } while (comma != NULL);
2724
2725 if (!missingConfigs.empty()) {
2726 String8 configStr;
2727 for (set<String8>::iterator iter = missingConfigs.begin();
2728 iter != missingConfigs.end();
2729 iter++) {
2730 configStr.appendFormat(" %s", iter->string());
2731 }
2732 SourcePos().warning("string '%s' is missing %u required localizations:%s",
2733 String8(nameIter->first).string(),
2734 (unsigned int)missingConfigs.size(),
2735 configStr.string());
2736 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002737 }
2738 }
2739
2740 return err;
2741}
2742
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002743status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
2744{
2745 ResourceFilter filter;
2746 status_t err = filter.parse(bundle->getConfigurations());
2747 if (err != NO_ERROR) {
2748 return err;
2749 }
2750
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002751 const ConfigDescription nullConfig;
2752
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002753 const size_t N = mOrderedPackages.size();
2754 size_t pi;
2755
Kenny Root7c710232010-11-22 22:28:37 -08002756 const static String16 mipmap16("mipmap");
2757
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002758 bool useUTF8 = !bundle->getUTF16StringsOption();
Kenny Root1741cd42010-03-18 12:12:11 -07002759
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002760 // Iterate through all data, collecting all values (strings,
2761 // references, etc).
Jeff Brown345b7eb2012-03-16 15:25:17 -07002762 StringPool valueStrings(useUTF8);
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002763 Vector<sp<Entry> > allEntries;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002764 for (pi=0; pi<N; pi++) {
2765 sp<Package> p = mOrderedPackages.itemAt(pi);
2766 if (p->getTypes().size() == 0) {
2767 // Empty, skip!
2768 continue;
2769 }
2770
Jeff Brown345b7eb2012-03-16 15:25:17 -07002771 StringPool typeStrings(useUTF8);
2772 StringPool keyStrings(useUTF8);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002773
2774 const size_t N = p->getOrderedTypes().size();
2775 for (size_t ti=0; ti<N; ti++) {
2776 sp<Type> t = p->getOrderedTypes().itemAt(ti);
2777 if (t == NULL) {
2778 typeStrings.add(String16("<empty>"), false);
2779 continue;
2780 }
Kenny Root7c710232010-11-22 22:28:37 -08002781 const String16 typeName(t->getName());
2782 typeStrings.add(typeName, false);
2783
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002784 // This is a hack to tweak the sorting order of the final strings,
2785 // to put stuff that is generally not language-specific first.
2786 String8 configTypeName(typeName);
2787 if (configTypeName == "drawable" || configTypeName == "layout"
2788 || configTypeName == "color" || configTypeName == "anim"
2789 || configTypeName == "interpolator" || configTypeName == "animator"
2790 || configTypeName == "xml" || configTypeName == "menu"
2791 || configTypeName == "mipmap" || configTypeName == "raw") {
2792 configTypeName = "1complex";
2793 } else {
2794 configTypeName = "2value";
2795 }
2796
Kenny Root7c710232010-11-22 22:28:37 -08002797 const bool filterable = (typeName != mipmap16);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002798
2799 const size_t N = t->getOrderedConfigs().size();
2800 for (size_t ci=0; ci<N; ci++) {
2801 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2802 if (c == NULL) {
2803 continue;
2804 }
2805 const size_t N = c->getEntries().size();
2806 for (size_t ei=0; ei<N; ei++) {
2807 ConfigDescription config = c->getEntries().keyAt(ei);
Kenny Root7c710232010-11-22 22:28:37 -08002808 if (filterable && !filter.match(config)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002809 continue;
2810 }
2811 sp<Entry> e = c->getEntries().valueAt(ei);
2812 if (e == NULL) {
2813 continue;
2814 }
2815 e->setNameIndex(keyStrings.add(e->getName(), true));
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002816
2817 // If this entry has no values for other configs,
2818 // and is the default config, then it is special. Otherwise
2819 // we want to add it with the config info.
2820 ConfigDescription* valueConfig = NULL;
2821 if (N != 1 || config == nullConfig) {
2822 valueConfig = &config;
2823 }
2824
2825 status_t err = e->prepareFlatten(&valueStrings, this,
2826 &configTypeName, &config);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002827 if (err != NO_ERROR) {
2828 return err;
2829 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002830 allEntries.add(e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002831 }
2832 }
2833 }
2834
2835 p->setTypeStrings(typeStrings.createStringBlock());
2836 p->setKeyStrings(keyStrings.createStringBlock());
2837 }
2838
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002839 if (bundle->getOutputAPKFile() != NULL) {
2840 // Now we want to sort the value strings for better locality. This will
2841 // cause the positions of the strings to change, so we need to go back
2842 // through out resource entries and update them accordingly. Only need
2843 // to do this if actually writing the output file.
2844 valueStrings.sortByConfig();
2845 for (pi=0; pi<allEntries.size(); pi++) {
2846 allEntries[pi]->remapStringValue(&valueStrings);
2847 }
2848 }
2849
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002850 ssize_t strAmt = 0;
2851
2852 // Now build the array of package chunks.
2853 Vector<sp<AaptFile> > flatPackages;
2854 for (pi=0; pi<N; pi++) {
2855 sp<Package> p = mOrderedPackages.itemAt(pi);
2856 if (p->getTypes().size() == 0) {
2857 // Empty, skip!
2858 continue;
2859 }
2860
2861 const size_t N = p->getTypeStrings().size();
2862
2863 const size_t baseSize = sizeof(ResTable_package);
2864
2865 // Start the package data.
2866 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
2867 ResTable_package* header = (ResTable_package*)data->editData(baseSize);
2868 if (header == NULL) {
2869 fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
2870 return NO_MEMORY;
2871 }
2872 memset(header, 0, sizeof(*header));
2873 header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
2874 header->header.headerSize = htods(sizeof(*header));
2875 header->id = htodl(p->getAssignedId());
2876 strcpy16_htod(header->name, p->getName().string());
2877
2878 // Write the string blocks.
2879 const size_t typeStringsStart = data->getSize();
2880 sp<AaptFile> strFile = p->getTypeStringsData();
2881 ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
Andreas Gampe2412f842014-09-30 20:55:57 -07002882 if (kPrintStringMetrics) {
2883 fprintf(stderr, "**** type strings: %zd\n", SSIZE(amt));
2884 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002885 strAmt += amt;
2886 if (amt < 0) {
2887 return amt;
2888 }
2889 const size_t keyStringsStart = data->getSize();
2890 strFile = p->getKeyStringsData();
2891 amt = data->writeData(strFile->getData(), strFile->getSize());
Andreas Gampe2412f842014-09-30 20:55:57 -07002892 if (kPrintStringMetrics) {
2893 fprintf(stderr, "**** key strings: %zd\n", SSIZE(amt));
2894 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002895 strAmt += amt;
2896 if (amt < 0) {
2897 return amt;
2898 }
2899
2900 // Build the type chunks inside of this package.
2901 for (size_t ti=0; ti<N; ti++) {
2902 // Retrieve them in the same order as the type string block.
2903 size_t len;
2904 String16 typeName(p->getTypeStrings().stringAt(ti, &len));
2905 sp<Type> t = p->getTypes().valueFor(typeName);
2906 LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
2907 "Type name %s not found",
2908 String8(typeName).string());
2909
Kenny Root7c710232010-11-22 22:28:37 -08002910 const bool filterable = (typeName != mipmap16);
2911
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002912 const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
MÃ¥rten Kongstad249e3ed2012-02-08 10:22:47 +01002913
2914 // Until a non-NO_ENTRY value has been written for a resource,
2915 // that resource is invalid; validResources[i] represents
2916 // the item at t->getOrderedConfigs().itemAt(i).
2917 Vector<bool> validResources;
2918 validResources.insertAt(false, 0, N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002919
2920 // First write the typeSpec chunk, containing information about
2921 // each resource entry in this type.
2922 {
2923 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
2924 const size_t typeSpecStart = data->getSize();
2925 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
2926 (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
2927 if (tsHeader == NULL) {
2928 fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
2929 return NO_MEMORY;
2930 }
2931 memset(tsHeader, 0, sizeof(*tsHeader));
2932 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
2933 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
2934 tsHeader->header.size = htodl(typeSpecSize);
2935 tsHeader->id = ti+1;
2936 tsHeader->entryCount = htodl(N);
2937
2938 uint32_t* typeSpecFlags = (uint32_t*)
2939 (((uint8_t*)data->editData())
2940 + typeSpecStart + sizeof(ResTable_typeSpec));
2941 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
Kenny Root7c710232010-11-22 22:28:37 -08002942
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002943 for (size_t ei=0; ei<N; ei++) {
2944 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2945 if (cl->getPublic()) {
2946 typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
2947 }
2948 const size_t CN = cl->getEntries().size();
2949 for (size_t ci=0; ci<CN; ci++) {
Kenny Root7c710232010-11-22 22:28:37 -08002950 if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002951 continue;
2952 }
2953 for (size_t cj=ci+1; cj<CN; cj++) {
Kenny Root7c710232010-11-22 22:28:37 -08002954 if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002955 continue;
2956 }
2957 typeSpecFlags[ei] |= htodl(
2958 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
2959 }
2960 }
2961 }
2962 }
2963
2964 // We need to write one type chunk for each configuration for
2965 // which we have entries in this type.
2966 const size_t NC = t->getUniqueConfigs().size();
2967
2968 const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
2969
2970 for (size_t ci=0; ci<NC; ci++) {
2971 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
2972
Andreas Gampe2412f842014-09-30 20:55:57 -07002973 if (kIsDebug) {
2974 printf("Writing config %zu config: imsi:%d/%d lang:%c%c cnt:%c%c "
2975 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
2976 "sw%ddp w%ddp h%ddp layout:%d\n",
2977 ti + 1,
2978 config.mcc, config.mnc,
2979 config.language[0] ? config.language[0] : '-',
2980 config.language[1] ? config.language[1] : '-',
2981 config.country[0] ? config.country[0] : '-',
2982 config.country[1] ? config.country[1] : '-',
2983 config.orientation,
2984 config.uiMode,
2985 config.touchscreen,
2986 config.density,
2987 config.keyboard,
2988 config.inputFlags,
2989 config.navigation,
2990 config.screenWidth,
2991 config.screenHeight,
2992 config.smallestScreenWidthDp,
2993 config.screenWidthDp,
2994 config.screenHeightDp,
2995 config.screenLayout);
2996 }
2997
Kenny Root7c710232010-11-22 22:28:37 -08002998 if (filterable && !filter.match(config)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002999 continue;
3000 }
3001
3002 const size_t typeStart = data->getSize();
3003
3004 ResTable_type* tHeader = (ResTable_type*)
3005 (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
3006 if (tHeader == NULL) {
3007 fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
3008 return NO_MEMORY;
3009 }
3010
3011 memset(tHeader, 0, sizeof(*tHeader));
3012 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
3013 tHeader->header.headerSize = htods(sizeof(*tHeader));
3014 tHeader->id = ti+1;
3015 tHeader->entryCount = htodl(N);
3016 tHeader->entriesStart = htodl(typeSize);
3017 tHeader->config = config;
Andreas Gampe2412f842014-09-30 20:55:57 -07003018 if (kIsDebug) {
3019 printf("Writing type %zu config: imsi:%d/%d lang:%c%c cnt:%c%c "
3020 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
3021 "sw%ddp w%ddp h%ddp layout:%d\n",
3022 ti + 1,
3023 tHeader->config.mcc, tHeader->config.mnc,
3024 tHeader->config.language[0] ? tHeader->config.language[0] : '-',
3025 tHeader->config.language[1] ? tHeader->config.language[1] : '-',
3026 tHeader->config.country[0] ? tHeader->config.country[0] : '-',
3027 tHeader->config.country[1] ? tHeader->config.country[1] : '-',
3028 tHeader->config.orientation,
3029 tHeader->config.uiMode,
3030 tHeader->config.touchscreen,
3031 tHeader->config.density,
3032 tHeader->config.keyboard,
3033 tHeader->config.inputFlags,
3034 tHeader->config.navigation,
3035 tHeader->config.screenWidth,
3036 tHeader->config.screenHeight,
3037 tHeader->config.smallestScreenWidthDp,
3038 tHeader->config.screenWidthDp,
3039 tHeader->config.screenHeightDp,
3040 tHeader->config.screenLayout);
3041 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003042 tHeader->config.swapHtoD();
3043
3044 // Build the entries inside of this type.
3045 for (size_t ei=0; ei<N; ei++) {
3046 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
3047 sp<Entry> e = cl->getEntries().valueFor(config);
3048
3049 // Set the offset for this entry in its type.
3050 uint32_t* index = (uint32_t*)
3051 (((uint8_t*)data->editData())
3052 + typeStart + sizeof(ResTable_type));
3053 if (e != NULL) {
3054 index[ei] = htodl(data->getSize()-typeStart-typeSize);
3055
3056 // Create the entry.
3057 ssize_t amt = e->flatten(bundle, data, cl->getPublic());
3058 if (amt < 0) {
3059 return amt;
3060 }
MÃ¥rten Kongstad249e3ed2012-02-08 10:22:47 +01003061 validResources.editItemAt(ei) = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003062 } else {
3063 index[ei] = htodl(ResTable_type::NO_ENTRY);
3064 }
3065 }
3066
3067 // Fill in the rest of the type information.
3068 tHeader = (ResTable_type*)
3069 (((uint8_t*)data->editData()) + typeStart);
3070 tHeader->header.size = htodl(data->getSize()-typeStart);
3071 }
MÃ¥rten Kongstad249e3ed2012-02-08 10:22:47 +01003072
3073 for (size_t i = 0; i < N; ++i) {
3074 if (!validResources[i]) {
3075 sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
3076 fprintf(stderr, "warning: no entries written for %s/%s\n",
3077 String8(typeName).string(), String8(c->getName()).string());
3078 }
3079 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003080 }
3081
3082 // Fill in the rest of the package information.
3083 header = (ResTable_package*)data->editData();
3084 header->header.size = htodl(data->getSize());
3085 header->typeStrings = htodl(typeStringsStart);
3086 header->lastPublicType = htodl(p->getTypeStrings().size());
3087 header->keyStrings = htodl(keyStringsStart);
3088 header->lastPublicKey = htodl(p->getKeyStrings().size());
3089
3090 flatPackages.add(data);
3091 }
3092
3093 // And now write out the final chunks.
3094 const size_t dataStart = dest->getSize();
3095
3096 {
3097 // blah
3098 ResTable_header header;
3099 memset(&header, 0, sizeof(header));
3100 header.header.type = htods(RES_TABLE_TYPE);
3101 header.header.headerSize = htods(sizeof(header));
3102 header.packageCount = htodl(flatPackages.size());
3103 status_t err = dest->writeData(&header, sizeof(header));
3104 if (err != NO_ERROR) {
3105 fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
3106 return err;
3107 }
3108 }
3109
3110 ssize_t strStart = dest->getSize();
3111 err = valueStrings.writeStringBlock(dest);
3112 if (err != NO_ERROR) {
3113 return err;
3114 }
3115
3116 ssize_t amt = (dest->getSize()-strStart);
3117 strAmt += amt;
Andreas Gampe2412f842014-09-30 20:55:57 -07003118 if (kPrintStringMetrics) {
3119 fprintf(stderr, "**** value strings: %zd\n", SSIZE(amt));
3120 fprintf(stderr, "**** total strings: %zd\n", SSIZE(strAmt));
3121 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003122
3123 for (pi=0; pi<flatPackages.size(); pi++) {
3124 err = dest->writeData(flatPackages[pi]->getData(),
3125 flatPackages[pi]->getSize());
3126 if (err != NO_ERROR) {
3127 fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
3128 return err;
3129 }
3130 }
3131
3132 ResTable_header* header = (ResTable_header*)
3133 (((uint8_t*)dest->getData()) + dataStart);
3134 header->header.size = htodl(dest->getSize() - dataStart);
3135
Andreas Gampe2412f842014-09-30 20:55:57 -07003136 if (kPrintStringMetrics) {
3137 fprintf(stderr, "**** total resource table size: %zu / %zu%% strings\n",
3138 dest->getSize(), (size_t)(strAmt*100)/dest->getSize());
3139 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003140
3141 return NO_ERROR;
3142}
3143
3144void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
3145{
3146 fprintf(fp,
3147 "<!-- This file contains <public> resource definitions for all\n"
3148 " resources that were generated from the source data. -->\n"
3149 "\n"
3150 "<resources>\n");
3151
3152 writePublicDefinitions(package, fp, true);
3153 writePublicDefinitions(package, fp, false);
3154
3155 fprintf(fp,
3156 "\n"
3157 "</resources>\n");
3158}
3159
3160void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
3161{
3162 bool didHeader = false;
3163
3164 sp<Package> pkg = mPackages.valueFor(package);
3165 if (pkg != NULL) {
3166 const size_t NT = pkg->getOrderedTypes().size();
3167 for (size_t i=0; i<NT; i++) {
3168 sp<Type> t = pkg->getOrderedTypes().itemAt(i);
3169 if (t == NULL) {
3170 continue;
3171 }
3172
3173 bool didType = false;
3174
3175 const size_t NC = t->getOrderedConfigs().size();
3176 for (size_t j=0; j<NC; j++) {
3177 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
3178 if (c == NULL) {
3179 continue;
3180 }
3181
3182 if (c->getPublic() != pub) {
3183 continue;
3184 }
3185
3186 if (!didType) {
3187 fprintf(fp, "\n");
3188 didType = true;
3189 }
3190 if (!didHeader) {
3191 if (pub) {
3192 fprintf(fp," <!-- PUBLIC SECTION. These resources have been declared public.\n");
3193 fprintf(fp," Changes to these definitions will break binary compatibility. -->\n\n");
3194 } else {
3195 fprintf(fp," <!-- PRIVATE SECTION. These resources have not been declared public.\n");
3196 fprintf(fp," You can make them public my moving these lines into a file in res/values. -->\n\n");
3197 }
3198 didHeader = true;
3199 }
3200 if (!pub) {
3201 const size_t NE = c->getEntries().size();
3202 for (size_t k=0; k<NE; k++) {
3203 const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
3204 if (pos.file != "") {
3205 fprintf(fp," <!-- Declared at %s:%d -->\n",
3206 pos.file.string(), pos.line);
3207 }
3208 }
3209 }
3210 fprintf(fp, " <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
3211 String8(t->getName()).string(),
3212 String8(c->getName()).string(),
3213 getResId(pkg, t, c->getEntryIndex()));
3214 }
3215 }
3216 }
3217}
3218
3219ResourceTable::Item::Item(const SourcePos& _sourcePos,
3220 bool _isId,
3221 const String16& _value,
3222 const Vector<StringPool::entry_style_span>* _style,
3223 int32_t _format)
3224 : sourcePos(_sourcePos)
3225 , isId(_isId)
3226 , value(_value)
3227 , format(_format)
3228 , bagKeyId(0)
3229 , evaluating(false)
3230{
3231 if (_style) {
3232 style = *_style;
3233 }
3234}
3235
3236status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
3237{
3238 if (mType == TYPE_BAG) {
3239 return NO_ERROR;
3240 }
3241 if (mType == TYPE_UNKNOWN) {
3242 mType = TYPE_BAG;
3243 return NO_ERROR;
3244 }
3245 sourcePos.error("Resource entry %s is already defined as a single item.\n"
3246 "%s:%d: Originally defined here.\n",
3247 String8(mName).string(),
3248 mItem.sourcePos.file.string(), mItem.sourcePos.line);
3249 return UNKNOWN_ERROR;
3250}
3251
3252status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
3253 const String16& value,
3254 const Vector<StringPool::entry_style_span>* style,
3255 int32_t format,
3256 const bool overwrite)
3257{
3258 Item item(sourcePos, false, value, style);
3259
3260 if (mType == TYPE_BAG) {
3261 const Item& item(mBag.valueAt(0));
3262 sourcePos.error("Resource entry %s is already defined as a bag.\n"
3263 "%s:%d: Originally defined here.\n",
3264 String8(mName).string(),
3265 item.sourcePos.file.string(), item.sourcePos.line);
3266 return UNKNOWN_ERROR;
3267 }
3268 if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
3269 sourcePos.error("Resource entry %s is already defined.\n"
3270 "%s:%d: Originally defined here.\n",
3271 String8(mName).string(),
3272 mItem.sourcePos.file.string(), mItem.sourcePos.line);
3273 return UNKNOWN_ERROR;
3274 }
Robert Greenwaltf878e2d2009-06-09 09:14:20 -07003275
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003276 mType = TYPE_ITEM;
3277 mItem = item;
3278 mItemFormat = format;
3279 return NO_ERROR;
3280}
3281
3282status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
3283 const String16& key, const String16& value,
3284 const Vector<StringPool::entry_style_span>* style,
3285 bool replace, bool isId, int32_t format)
3286{
3287 status_t err = makeItABag(sourcePos);
3288 if (err != NO_ERROR) {
3289 return err;
3290 }
3291
3292 Item item(sourcePos, isId, value, style, format);
3293
3294 // XXX NOTE: there is an error if you try to have a bag with two keys,
3295 // one an attr and one an id, with the same name. Not something we
3296 // currently ever have to worry about.
3297 ssize_t origKey = mBag.indexOfKey(key);
3298 if (origKey >= 0) {
3299 if (!replace) {
3300 const Item& item(mBag.valueAt(origKey));
3301 sourcePos.error("Resource entry %s already has bag item %s.\n"
3302 "%s:%d: Originally defined here.\n",
3303 String8(mName).string(), String8(key).string(),
3304 item.sourcePos.file.string(), item.sourcePos.line);
3305 return UNKNOWN_ERROR;
3306 }
3307 //printf("Replacing %s with %s\n",
3308 // String8(mBag.valueFor(key).value).string(), String8(value).string());
3309 mBag.replaceValueFor(key, item);
3310 }
3311
3312 mBag.add(key, item);
3313 return NO_ERROR;
3314}
3315
Robert Greenwalt4b4f4a92009-04-02 16:55:50 -07003316status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
3317{
3318 status_t err = makeItABag(sourcePos);
3319 if (err != NO_ERROR) {
3320 return err;
3321 }
3322
3323 mBag.clear();
3324 return NO_ERROR;
3325}
3326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003327status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
3328 const String16& package)
3329{
3330 const String16 attr16("attr");
3331 const String16 id16("id");
3332 const size_t N = mBag.size();
3333 for (size_t i=0; i<N; i++) {
3334 const String16& key = mBag.keyAt(i);
3335 const Item& it = mBag.valueAt(i);
3336 if (it.isId) {
3337 if (!table->hasBagOrEntry(key, &id16, &package)) {
3338 String16 value("false");
3339 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
3340 id16, key, value);
3341 if (err != NO_ERROR) {
3342 return err;
3343 }
3344 }
3345 } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
3346
3347#if 1
3348// fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
3349// String8(key).string());
3350// const Item& item(mBag.valueAt(i));
3351// fprintf(stderr, "Referenced from file %s line %d\n",
3352// item.sourcePos.file.string(), item.sourcePos.line);
3353// return UNKNOWN_ERROR;
3354#else
3355 char numberStr[16];
3356 sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
3357 status_t err = table->addBag(SourcePos("<generated>", 0), package,
3358 attr16, key, String16(""),
3359 String16("^type"),
3360 String16(numberStr), NULL, NULL);
3361 if (err != NO_ERROR) {
3362 return err;
3363 }
3364#endif
3365 }
3366 }
3367 return NO_ERROR;
3368}
3369
3370status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
Andreas Gampe2412f842014-09-30 20:55:57 -07003371 const String16& /* package */)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003372{
3373 bool hasErrors = false;
3374
3375 if (mType == TYPE_BAG) {
3376 const char* errorMsg;
3377 const String16 style16("style");
3378 const String16 attr16("attr");
3379 const String16 id16("id");
3380 mParentId = 0;
3381 if (mParent.size() > 0) {
Dianne Hackbornf795e9a2011-06-09 16:17:53 -07003382 mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003383 if (mParentId == 0) {
3384 mPos.error("Error retrieving parent for item: %s '%s'.\n",
3385 errorMsg, String8(mParent).string());
3386 hasErrors = true;
3387 }
3388 }
3389 const size_t N = mBag.size();
3390 for (size_t i=0; i<N; i++) {
3391 const String16& key = mBag.keyAt(i);
3392 Item& it = mBag.editValueAt(i);
3393 it.bagKeyId = table->getResId(key,
Dianne Hackbornf795e9a2011-06-09 16:17:53 -07003394 it.isId ? &id16 : &attr16, NULL, &errorMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003395 //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
3396 if (it.bagKeyId == 0) {
3397 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
3398 String8(it.isId ? id16 : attr16).string(),
3399 String8(key).string());
3400 hasErrors = true;
3401 }
3402 }
3403 }
Andreas Gampe2412f842014-09-30 20:55:57 -07003404 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003405}
3406
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003407status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table,
3408 const String8* configTypeName, const ConfigDescription* config)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003409{
3410 if (mType == TYPE_ITEM) {
3411 Item& it = mItem;
3412 AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
3413 if (!table->stringToValue(&it.parsedValue, strings,
3414 it.value, false, true, 0,
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003415 &it.style, NULL, &ac, mItemFormat,
3416 configTypeName, config)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003417 return UNKNOWN_ERROR;
3418 }
3419 } else if (mType == TYPE_BAG) {
3420 const size_t N = mBag.size();
3421 for (size_t i=0; i<N; i++) {
3422 const String16& key = mBag.keyAt(i);
3423 Item& it = mBag.editValueAt(i);
3424 AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
3425 if (!table->stringToValue(&it.parsedValue, strings,
3426 it.value, false, true, it.bagKeyId,
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003427 &it.style, NULL, &ac, it.format,
3428 configTypeName, config)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003429 return UNKNOWN_ERROR;
3430 }
3431 }
3432 } else {
3433 mPos.error("Error: entry %s is not a single item or a bag.\n",
3434 String8(mName).string());
3435 return UNKNOWN_ERROR;
3436 }
3437 return NO_ERROR;
3438}
3439
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003440status_t ResourceTable::Entry::remapStringValue(StringPool* strings)
3441{
3442 if (mType == TYPE_ITEM) {
3443 Item& it = mItem;
3444 if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
3445 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
3446 }
3447 } else if (mType == TYPE_BAG) {
3448 const size_t N = mBag.size();
3449 for (size_t i=0; i<N; i++) {
3450 Item& it = mBag.editValueAt(i);
3451 if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
3452 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
3453 }
3454 }
3455 } else {
3456 mPos.error("Error: entry %s is not a single item or a bag.\n",
3457 String8(mName).string());
3458 return UNKNOWN_ERROR;
3459 }
3460 return NO_ERROR;
3461}
3462
Andreas Gampe2412f842014-09-30 20:55:57 -07003463ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp<AaptFile>& data, bool isPublic)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003464{
3465 size_t amt = 0;
3466 ResTable_entry header;
3467 memset(&header, 0, sizeof(header));
3468 header.size = htods(sizeof(header));
Andreas Gampe2412f842014-09-30 20:55:57 -07003469 const type ty = mType;
3470 if (ty == TYPE_BAG) {
3471 header.flags |= htods(header.FLAG_COMPLEX);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003472 }
Andreas Gampe2412f842014-09-30 20:55:57 -07003473 if (isPublic) {
3474 header.flags |= htods(header.FLAG_PUBLIC);
3475 }
3476 header.key.index = htodl(mNameIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003477 if (ty != TYPE_BAG) {
3478 status_t err = data->writeData(&header, sizeof(header));
3479 if (err != NO_ERROR) {
3480 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3481 return err;
3482 }
3483
3484 const Item& it = mItem;
3485 Res_value par;
3486 memset(&par, 0, sizeof(par));
3487 par.size = htods(it.parsedValue.size);
3488 par.dataType = it.parsedValue.dataType;
3489 par.res0 = it.parsedValue.res0;
3490 par.data = htodl(it.parsedValue.data);
3491 #if 0
3492 printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
3493 String8(mName).string(), it.parsedValue.dataType,
3494 it.parsedValue.data, par.res0);
3495 #endif
3496 err = data->writeData(&par, it.parsedValue.size);
3497 if (err != NO_ERROR) {
3498 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3499 return err;
3500 }
3501 amt += it.parsedValue.size;
3502 } else {
3503 size_t N = mBag.size();
3504 size_t i;
3505 // Create correct ordering of items.
3506 KeyedVector<uint32_t, const Item*> items;
3507 for (i=0; i<N; i++) {
3508 const Item& it = mBag.valueAt(i);
3509 items.add(it.bagKeyId, &it);
3510 }
3511 N = items.size();
3512
3513 ResTable_map_entry mapHeader;
3514 memcpy(&mapHeader, &header, sizeof(header));
3515 mapHeader.size = htods(sizeof(mapHeader));
3516 mapHeader.parent.ident = htodl(mParentId);
3517 mapHeader.count = htodl(N);
3518 status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
3519 if (err != NO_ERROR) {
3520 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3521 return err;
3522 }
3523
3524 for (i=0; i<N; i++) {
3525 const Item& it = *items.valueAt(i);
3526 ResTable_map map;
3527 map.name.ident = htodl(it.bagKeyId);
3528 map.value.size = htods(it.parsedValue.size);
3529 map.value.dataType = it.parsedValue.dataType;
3530 map.value.res0 = it.parsedValue.res0;
3531 map.value.data = htodl(it.parsedValue.data);
3532 err = data->writeData(&map, sizeof(map));
3533 if (err != NO_ERROR) {
3534 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3535 return err;
3536 }
3537 amt += sizeof(map);
3538 }
3539 }
3540 return amt;
3541}
3542
3543void ResourceTable::ConfigList::appendComment(const String16& comment,
3544 bool onlyIfEmpty)
3545{
3546 if (comment.size() <= 0) {
3547 return;
3548 }
3549 if (onlyIfEmpty && mComment.size() > 0) {
3550 return;
3551 }
3552 if (mComment.size() > 0) {
3553 mComment.append(String16("\n"));
3554 }
3555 mComment.append(comment);
3556}
3557
3558void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
3559{
3560 if (comment.size() <= 0) {
3561 return;
3562 }
3563 if (mTypeComment.size() > 0) {
3564 mTypeComment.append(String16("\n"));
3565 }
3566 mTypeComment.append(comment);
3567}
3568
3569status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
3570 const String16& name,
3571 const uint32_t ident)
3572{
3573 #if 0
3574 int32_t entryIdx = Res_GETENTRY(ident);
3575 if (entryIdx < 0) {
3576 sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
3577 String8(mName).string(), String8(name).string(), ident);
3578 return UNKNOWN_ERROR;
3579 }
3580 #endif
3581
3582 int32_t typeIdx = Res_GETTYPE(ident);
3583 if (typeIdx >= 0) {
3584 typeIdx++;
3585 if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
3586 sourcePos.error("Public resource %s/%s has conflicting type codes for its"
3587 " public identifiers (0x%x vs 0x%x).\n",
3588 String8(mName).string(), String8(name).string(),
3589 mPublicIndex, typeIdx);
3590 return UNKNOWN_ERROR;
3591 }
3592 mPublicIndex = typeIdx;
3593 }
3594
3595 if (mFirstPublicSourcePos == NULL) {
3596 mFirstPublicSourcePos = new SourcePos(sourcePos);
3597 }
3598
3599 if (mPublic.indexOfKey(name) < 0) {
3600 mPublic.add(name, Public(sourcePos, String16(), ident));
3601 } else {
3602 Public& p = mPublic.editValueFor(name);
3603 if (p.ident != ident) {
3604 sourcePos.error("Public resource %s/%s has conflicting public identifiers"
3605 " (0x%08x vs 0x%08x).\n"
3606 "%s:%d: Originally defined here.\n",
3607 String8(mName).string(), String8(name).string(), p.ident, ident,
3608 p.sourcePos.file.string(), p.sourcePos.line);
3609 return UNKNOWN_ERROR;
3610 }
3611 }
3612
3613 return NO_ERROR;
3614}
3615
Dianne Hackborn58c27a02009-08-13 13:36:00 -07003616void ResourceTable::Type::canAddEntry(const String16& name)
3617{
3618 mCanAddEntries.add(name);
3619}
3620
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003621sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
3622 const SourcePos& sourcePos,
3623 const ResTable_config* config,
Robert Greenwaltf878e2d2009-06-09 09:14:20 -07003624 bool doSetIndex,
Xavier Ducrohet99080c72010-02-04 18:45:31 -08003625 bool overlay,
3626 bool autoAddOverlay)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003627{
3628 int pos = -1;
3629 sp<ConfigList> c = mConfigs.valueFor(entry);
3630 if (c == NULL) {
Xavier Ducrohet99080c72010-02-04 18:45:31 -08003631 if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
Dianne Hackborn58c27a02009-08-13 13:36:00 -07003632 sourcePos.error("Resource at %s appears in overlay but not"
3633 " in the base package; use <add-resource> to add.\n",
3634 String8(entry).string());
Robert Greenwaltf878e2d2009-06-09 09:14:20 -07003635 return NULL;
3636 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003637 c = new ConfigList(entry, sourcePos);
3638 mConfigs.add(entry, c);
3639 pos = (int)mOrderedConfigs.size();
3640 mOrderedConfigs.add(c);
3641 if (doSetIndex) {
3642 c->setEntryIndex(pos);
3643 }
3644 }
3645
3646 ConfigDescription cdesc;
3647 if (config) cdesc = *config;
3648
3649 sp<Entry> e = c->getEntries().valueFor(cdesc);
3650 if (e == NULL) {
Andreas Gampe2412f842014-09-30 20:55:57 -07003651 if (kIsDebug) {
3652 if (config != NULL) {
3653 printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
Dianne Hackborn69cb8752011-05-19 18:13:32 -07003654 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
Andreas Gampe2412f842014-09-30 20:55:57 -07003655 "sw%ddp w%ddp h%ddp layout:%d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003656 sourcePos.file.string(), sourcePos.line,
3657 config->mcc, config->mnc,
3658 config->language[0] ? config->language[0] : '-',
3659 config->language[1] ? config->language[1] : '-',
3660 config->country[0] ? config->country[0] : '-',
3661 config->country[1] ? config->country[1] : '-',
3662 config->orientation,
3663 config->touchscreen,
3664 config->density,
3665 config->keyboard,
3666 config->inputFlags,
3667 config->navigation,
3668 config->screenWidth,
Dianne Hackbornebff8f92011-05-12 18:07:47 -07003669 config->screenHeight,
Dianne Hackborn69cb8752011-05-19 18:13:32 -07003670 config->smallestScreenWidthDp,
Dianne Hackbornebff8f92011-05-12 18:07:47 -07003671 config->screenWidthDp,
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07003672 config->screenHeightDp,
Andreas Gampe2412f842014-09-30 20:55:57 -07003673 config->screenLayout);
3674 } else {
3675 printf("New entry at %s:%d: NULL config\n",
3676 sourcePos.file.string(), sourcePos.line);
3677 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003678 }
3679 e = new Entry(entry, sourcePos);
3680 c->addEntry(cdesc, e);
3681 /*
3682 if (doSetIndex) {
3683 if (pos < 0) {
3684 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
3685 if (mOrderedConfigs[pos] == c) {
3686 break;
3687 }
3688 }
3689 if (pos >= (int)mOrderedConfigs.size()) {
3690 sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
3691 return NULL;
3692 }
3693 }
3694 e->setEntryIndex(pos);
3695 }
3696 */
3697 }
3698
3699 mUniqueConfigs.add(cdesc);
3700
3701 return e;
3702}
3703
3704status_t ResourceTable::Type::applyPublicEntryOrder()
3705{
3706 size_t N = mOrderedConfigs.size();
3707 Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
3708 bool hasError = false;
3709
3710 size_t i;
3711 for (i=0; i<N; i++) {
3712 mOrderedConfigs.replaceAt(NULL, i);
3713 }
3714
3715 const size_t NP = mPublic.size();
3716 //printf("Ordering %d configs from %d public defs\n", N, NP);
3717 size_t j;
3718 for (j=0; j<NP; j++) {
3719 const String16& name = mPublic.keyAt(j);
3720 const Public& p = mPublic.valueAt(j);
3721 int32_t idx = Res_GETENTRY(p.ident);
3722 //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
3723 // String8(mName).string(), String8(name).string(), p.ident, N);
3724 bool found = false;
3725 for (i=0; i<N; i++) {
3726 sp<ConfigList> e = origOrder.itemAt(i);
3727 //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
3728 if (e->getName() == name) {
3729 if (idx >= (int32_t)mOrderedConfigs.size()) {
3730 p.sourcePos.error("Public entry identifier 0x%x entry index "
3731 "is larger than available symbols (index %d, total symbols %d).\n",
3732 p.ident, idx, mOrderedConfigs.size());
3733 hasError = true;
3734 } else if (mOrderedConfigs.itemAt(idx) == NULL) {
3735 e->setPublic(true);
3736 e->setPublicSourcePos(p.sourcePos);
3737 mOrderedConfigs.replaceAt(e, idx);
3738 origOrder.removeAt(i);
3739 N--;
3740 found = true;
3741 break;
3742 } else {
3743 sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
3744
3745 p.sourcePos.error("Multiple entry names declared for public entry"
3746 " identifier 0x%x in type %s (%s vs %s).\n"
3747 "%s:%d: Originally defined here.",
3748 idx+1, String8(mName).string(),
3749 String8(oe->getName()).string(),
3750 String8(name).string(),
3751 oe->getPublicSourcePos().file.string(),
3752 oe->getPublicSourcePos().line);
3753 hasError = true;
3754 }
3755 }
3756 }
3757
3758 if (!found) {
3759 p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
3760 String8(mName).string(), String8(name).string());
3761 hasError = true;
3762 }
3763 }
3764
3765 //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
3766
3767 if (N != origOrder.size()) {
3768 printf("Internal error: remaining private symbol count mismatch\n");
3769 N = origOrder.size();
3770 }
3771
3772 j = 0;
3773 for (i=0; i<N; i++) {
3774 sp<ConfigList> e = origOrder.itemAt(i);
3775 // There will always be enough room for the remaining entries.
3776 while (mOrderedConfigs.itemAt(j) != NULL) {
3777 j++;
3778 }
3779 mOrderedConfigs.replaceAt(e, j);
3780 j++;
3781 }
3782
Andreas Gampe2412f842014-09-30 20:55:57 -07003783 return hasError ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003784}
3785
3786ResourceTable::Package::Package(const String16& name, ssize_t includedId)
3787 : mName(name), mIncludedId(includedId),
3788 mTypeStringsMapping(0xffffffff),
3789 mKeyStringsMapping(0xffffffff)
3790{
3791}
3792
3793sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
3794 const SourcePos& sourcePos,
3795 bool doSetIndex)
3796{
3797 sp<Type> t = mTypes.valueFor(type);
3798 if (t == NULL) {
3799 t = new Type(type, sourcePos);
3800 mTypes.add(type, t);
3801 mOrderedTypes.add(t);
3802 if (doSetIndex) {
3803 // For some reason the type's index is set to one plus the index
3804 // in the mOrderedTypes list, rather than just the index.
3805 t->setIndex(mOrderedTypes.size());
3806 }
3807 }
3808 return t;
3809}
3810
3811status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
3812{
3813 mTypeStringsData = data;
3814 status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
3815 if (err != NO_ERROR) {
3816 fprintf(stderr, "ERROR: Type string data is corrupt!\n");
3817 }
3818 return err;
3819}
3820
3821status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
3822{
3823 mKeyStringsData = data;
3824 status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
3825 if (err != NO_ERROR) {
3826 fprintf(stderr, "ERROR: Key string data is corrupt!\n");
3827 }
3828 return err;
3829}
3830
3831status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
3832 ResStringPool* strings,
3833 DefaultKeyedVector<String16, uint32_t>* mappings)
3834{
3835 if (data->getData() == NULL) {
3836 return UNKNOWN_ERROR;
3837 }
3838
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003839 status_t err = strings->setTo(data->getData(), data->getSize());
3840 if (err == NO_ERROR) {
3841 const size_t N = strings->size();
3842 for (size_t i=0; i<N; i++) {
3843 size_t len;
3844 mappings->add(String16(strings->stringAt(i, &len)), i);
3845 }
3846 }
3847 return err;
3848}
3849
3850status_t ResourceTable::Package::applyPublicTypeOrder()
3851{
3852 size_t N = mOrderedTypes.size();
3853 Vector<sp<Type> > origOrder(mOrderedTypes);
3854
3855 size_t i;
3856 for (i=0; i<N; i++) {
3857 mOrderedTypes.replaceAt(NULL, i);
3858 }
3859
3860 for (i=0; i<N; i++) {
3861 sp<Type> t = origOrder.itemAt(i);
3862 int32_t idx = t->getPublicIndex();
3863 if (idx > 0) {
3864 idx--;
3865 while (idx >= (int32_t)mOrderedTypes.size()) {
3866 mOrderedTypes.add();
3867 }
3868 if (mOrderedTypes.itemAt(idx) != NULL) {
3869 sp<Type> ot = mOrderedTypes.itemAt(idx);
3870 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
3871 " identifier 0x%x (%s vs %s).\n"
3872 "%s:%d: Originally defined here.",
3873 idx, String8(ot->getName()).string(),
3874 String8(t->getName()).string(),
3875 ot->getFirstPublicSourcePos().file.string(),
3876 ot->getFirstPublicSourcePos().line);
3877 return UNKNOWN_ERROR;
3878 }
3879 mOrderedTypes.replaceAt(t, idx);
3880 origOrder.removeAt(i);
3881 i--;
3882 N--;
3883 }
3884 }
3885
3886 size_t j=0;
3887 for (i=0; i<N; i++) {
3888 sp<Type> t = origOrder.itemAt(i);
3889 // There will always be enough room for the remaining types.
3890 while (mOrderedTypes.itemAt(j) != NULL) {
3891 j++;
3892 }
3893 mOrderedTypes.replaceAt(t, j);
3894 }
3895
3896 return NO_ERROR;
3897}
3898
3899sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
3900{
3901 sp<Package> p = mPackages.valueFor(package);
3902 if (p == NULL) {
MÃ¥rten Kongstadc761d8b2012-02-07 19:12:45 +01003903 if (mIsAppPackage) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003904 if (mHaveAppPackage) {
3905 fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
3906 "Use -x to create extended resources.\n");
3907 return NULL;
3908 }
3909 mHaveAppPackage = true;
3910 p = new Package(package, 127);
3911 } else {
3912 p = new Package(package, mNextPackageId);
3913 }
3914 //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
3915 // String8(package).string(), p->getAssignedId());
3916 mPackages.add(package, p);
3917 mOrderedPackages.add(p);
3918 mNextPackageId++;
3919 }
3920 return p;
3921}
3922
3923sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
3924 const String16& type,
3925 const SourcePos& sourcePos,
3926 bool doSetIndex)
3927{
3928 sp<Package> p = getPackage(package);
3929 if (p == NULL) {
3930 return NULL;
3931 }
3932 return p->getType(type, sourcePos, doSetIndex);
3933}
3934
3935sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
3936 const String16& type,
3937 const String16& name,
3938 const SourcePos& sourcePos,
Robert Greenwaltf878e2d2009-06-09 09:14:20 -07003939 bool overlay,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003940 const ResTable_config* config,
3941 bool doSetIndex)
3942{
3943 sp<Type> t = getType(package, type, sourcePos, doSetIndex);
3944 if (t == NULL) {
3945 return NULL;
3946 }
Xavier Ducrohet99080c72010-02-04 18:45:31 -08003947 return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003948}
3949
3950sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
3951 const ResTable_config* config) const
3952{
3953 int pid = Res_GETPACKAGE(resID)+1;
3954 const size_t N = mOrderedPackages.size();
3955 size_t i;
3956 sp<Package> p;
3957 for (i=0; i<N; i++) {
3958 sp<Package> check = mOrderedPackages[i];
3959 if (check->getAssignedId() == pid) {
3960 p = check;
3961 break;
3962 }
3963
3964 }
3965 if (p == NULL) {
Marco Nelissendd931862009-07-13 13:02:33 -07003966 fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003967 return NULL;
3968 }
3969
3970 int tid = Res_GETTYPE(resID);
3971 if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
Marco Nelissendd931862009-07-13 13:02:33 -07003972 fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003973 return NULL;
3974 }
3975 sp<Type> t = p->getOrderedTypes()[tid];
3976
3977 int eid = Res_GETENTRY(resID);
3978 if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
Marco Nelissendd931862009-07-13 13:02:33 -07003979 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003980 return NULL;
3981 }
3982
3983 sp<ConfigList> c = t->getOrderedConfigs()[eid];
3984 if (c == NULL) {
Marco Nelissendd931862009-07-13 13:02:33 -07003985 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003986 return NULL;
3987 }
3988
3989 ConfigDescription cdesc;
3990 if (config) cdesc = *config;
3991 sp<Entry> e = c->getEntries().valueFor(cdesc);
3992 if (c == NULL) {
Marco Nelissendd931862009-07-13 13:02:33 -07003993 fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003994 return NULL;
3995 }
3996
3997 return e;
3998}
3999
4000const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
4001{
4002 sp<const Entry> e = getEntry(resID);
4003 if (e == NULL) {
4004 return NULL;
4005 }
4006
4007 const size_t N = e->getBag().size();
4008 for (size_t i=0; i<N; i++) {
4009 const Item& it = e->getBag().valueAt(i);
4010 if (it.bagKeyId == 0) {
Marco Nelissendd931862009-07-13 13:02:33 -07004011 fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004012 String8(e->getName()).string(),
4013 String8(e->getBag().keyAt(i)).string());
4014 }
4015 if (it.bagKeyId == attrID) {
4016 return &it;
4017 }
4018 }
4019
4020 return NULL;
4021}
4022
4023bool ResourceTable::getItemValue(
4024 uint32_t resID, uint32_t attrID, Res_value* outValue)
4025{
4026 const Item* item = getItem(resID, attrID);
4027
4028 bool res = false;
4029 if (item != NULL) {
4030 if (item->evaluating) {
4031 sp<const Entry> e = getEntry(resID);
4032 const size_t N = e->getBag().size();
4033 size_t i;
4034 for (i=0; i<N; i++) {
4035 if (&e->getBag().valueAt(i) == item) {
4036 break;
4037 }
4038 }
Marco Nelissendd931862009-07-13 13:02:33 -07004039 fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004040 String8(e->getName()).string(),
4041 String8(e->getBag().keyAt(i)).string());
4042 return false;
4043 }
4044 item->evaluating = true;
4045 res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
Andreas Gampe2412f842014-09-30 20:55:57 -07004046 if (kIsDebug) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004047 if (res) {
4048 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
4049 resID, attrID, String8(getEntry(resID)->getName()).string(),
4050 outValue->dataType, outValue->data);
4051 } else {
4052 printf("getItemValue of #%08x[#%08x]: failed\n",
4053 resID, attrID);
4054 }
Andreas Gampe2412f842014-09-30 20:55:57 -07004055 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004056 item->evaluating = false;
4057 }
4058 return res;
4059}