in libresource/ResourceTypes.cpp [4785:5345]
bool ResTable::stringToValue(Res_value* outValue, String16* outString,
const char16_t* s, size_t len,
bool preserveSpaces, bool coerceType,
uint32_t attrID,
const String16* defType,
const String16* defPackage,
Accessor* accessor,
void* accessorCookie,
uint32_t attrType,
bool enforcePrivate) const
{
bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting();
const char* errorMsg = NULL;
outValue->size = sizeof(Res_value);
outValue->res0 = 0;
// First strip leading/trailing whitespace. Do this before handling
// escapes, so they can be used to force whitespace into the string.
if (!preserveSpaces) {
while (len > 0 && isspace16(*s)) {
s++;
len--;
}
while (len > 0 && isspace16(s[len-1])) {
len--;
}
// If the string ends with '\', then we keep the space after it.
if (len > 0 && s[len-1] == '\\' && s[len] != 0) {
len++;
}
}
//printf("Value for: %s\n", String8(s, len).string());
uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED;
uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff;
bool fromAccessor = false;
if (attrID != 0 && !Res_INTERNALID(attrID)) {
const ssize_t p = getResourcePackageIndex(attrID);
const bag_entry* bag;
ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
//printf("For attr 0x%08x got bag of %d\n", attrID, cnt);
if (cnt >= 0) {
while (cnt > 0) {
//printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data);
switch (bag->map.name.ident) {
case ResTable_map::ATTR_TYPE:
attrType = bag->map.value.data;
break;
case ResTable_map::ATTR_MIN:
attrMin = bag->map.value.data;
break;
case ResTable_map::ATTR_MAX:
attrMax = bag->map.value.data;
break;
case ResTable_map::ATTR_L10N:
l10nReq = bag->map.value.data;
break;
}
bag++;
cnt--;
}
unlockBag(bag);
} else if (accessor && accessor->getAttributeType(attrID, &attrType)) {
fromAccessor = true;
if (attrType == ResTable_map::TYPE_ENUM
|| attrType == ResTable_map::TYPE_FLAGS
|| attrType == ResTable_map::TYPE_INTEGER) {
accessor->getAttributeMin(attrID, &attrMin);
accessor->getAttributeMax(attrID, &attrMax);
}
if (localizationSetting) {
l10nReq = accessor->getAttributeL10N(attrID);
}
}
}
const bool canStringCoerce =
coerceType && (attrType&ResTable_map::TYPE_STRING) != 0;
if (*s == '@') {
outValue->dataType = outValue->TYPE_REFERENCE;
// Note: we don't check attrType here because the reference can
// be to any other type; we just need to count on the client making
// sure the referenced type is correct.
//printf("Looking up ref: %s\n", String8(s, len).string());
// It's a reference!
if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') {
// Special case @null as undefined. This will be converted by
// AssetManager to TYPE_NULL with data DATA_NULL_UNDEFINED.
outValue->data = 0;
return true;
} else if (len == 6 && s[1]=='e' && s[2]=='m' && s[3]=='p' && s[4]=='t' && s[5]=='y') {
// Special case @empty as explicitly defined empty value.
outValue->dataType = Res_value::TYPE_NULL;
outValue->data = Res_value::DATA_NULL_EMPTY;
return true;
} else {
bool createIfNotFound = false;
const char16_t* resourceRefName;
int resourceNameLen;
if (len > 2 && s[1] == '+') {
createIfNotFound = true;
resourceRefName = s + 2;
resourceNameLen = len - 2;
} else if (len > 2 && s[1] == '*') {
enforcePrivate = false;
resourceRefName = s + 2;
resourceNameLen = len - 2;
} else {
createIfNotFound = false;
resourceRefName = s + 1;
resourceNameLen = len - 1;
}
String16 package, type, name;
if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name,
defType, defPackage, &errorMsg)) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, errorMsg);
}
return false;
}
uint32_t specFlags = 0;
uint32_t rid = identifierForName(name.string(), name.size(), type.string(),
type.size(), package.string(), package.size(), &specFlags);
if (rid != 0) {
if (enforcePrivate) {
if (accessor == NULL || accessor->getAssetsPackage() != package) {
if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Resource is not public.");
}
return false;
}
}
}
if (accessor) {
rid = Res_MAKEID(
accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
Res_GETTYPE(rid), Res_GETENTRY(rid));
if (kDebugTableNoisy) {
ALOGI("Incl %s:%s/%s: 0x%08x\n",
String8(package).string(), String8(type).string(),
String8(name).string(), rid);
}
}
uint32_t packageId = Res_GETPACKAGE(rid) + 1;
if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
}
outValue->data = rid;
return true;
}
if (accessor) {
uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name,
createIfNotFound);
if (rid != 0) {
if (kDebugTableNoisy) {
ALOGI("Pckg %s:%s/%s: 0x%08x\n",
String8(package).string(), String8(type).string(),
String8(name).string(), rid);
}
uint32_t packageId = Res_GETPACKAGE(rid) + 1;
if (packageId == 0x00) {
outValue->data = rid;
outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
return true;
} else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
// We accept packageId's generated as 0x01 in order to support
// building the android system resources
outValue->data = rid;
return true;
}
}
}
}
if (accessor != NULL) {
accessor->reportError(accessorCookie, "No resource found that matches the given name");
}
return false;
}
// if we got to here, and localization is required and it's not a reference,
// complain and bail.
if (l10nReq == ResTable_map::L10N_SUGGESTED) {
if (localizationSetting) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "This attribute must be localized.");
}
}
}
if (*s == '#') {
// It's a color! Convert to an integer of the form 0xaarrggbb.
uint32_t color = 0;
bool error = false;
if (len == 4) {
outValue->dataType = outValue->TYPE_INT_COLOR_RGB4;
color |= 0xFF000000;
color |= get_hex(s[1], &error) << 20;
color |= get_hex(s[1], &error) << 16;
color |= get_hex(s[2], &error) << 12;
color |= get_hex(s[2], &error) << 8;
color |= get_hex(s[3], &error) << 4;
color |= get_hex(s[3], &error);
} else if (len == 5) {
outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4;
color |= get_hex(s[1], &error) << 28;
color |= get_hex(s[1], &error) << 24;
color |= get_hex(s[2], &error) << 20;
color |= get_hex(s[2], &error) << 16;
color |= get_hex(s[3], &error) << 12;
color |= get_hex(s[3], &error) << 8;
color |= get_hex(s[4], &error) << 4;
color |= get_hex(s[4], &error);
} else if (len == 7) {
outValue->dataType = outValue->TYPE_INT_COLOR_RGB8;
color |= 0xFF000000;
color |= get_hex(s[1], &error) << 20;
color |= get_hex(s[2], &error) << 16;
color |= get_hex(s[3], &error) << 12;
color |= get_hex(s[4], &error) << 8;
color |= get_hex(s[5], &error) << 4;
color |= get_hex(s[6], &error);
} else if (len == 9) {
outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8;
color |= get_hex(s[1], &error) << 28;
color |= get_hex(s[2], &error) << 24;
color |= get_hex(s[3], &error) << 20;
color |= get_hex(s[4], &error) << 16;
color |= get_hex(s[5], &error) << 12;
color |= get_hex(s[6], &error) << 8;
color |= get_hex(s[7], &error) << 4;
color |= get_hex(s[8], &error);
} else {
error = true;
}
if (!error) {
if ((attrType&ResTable_map::TYPE_COLOR) == 0) {
if (!canStringCoerce) {
if (accessor != NULL) {
accessor->reportError(accessorCookie,
"Color types not allowed");
}
return false;
}
} else {
outValue->data = color;
//printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color);
return true;
}
} else {
if ((attrType&ResTable_map::TYPE_COLOR) != 0) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Color value not valid --"
" must be #rgb, #argb, #rrggbb, or #aarrggbb");
}
#if 0
fprintf(stderr, "%s: Color ID %s value %s is not valid\n",
"Resource File", //(const char*)in->getPrintableSource(),
String8(*curTag).string(),
String8(s, len).string());
#endif
return false;
}
}
}
if (*s == '?') {
outValue->dataType = outValue->TYPE_ATTRIBUTE;
// Note: we don't check attrType here because the reference can
// be to any other type; we just need to count on the client making
// sure the referenced type is correct.
//printf("Looking up attr: %s\n", String8(s, len).string());
static const String16 attr16("attr");
String16 package, type, name;
if (!expandResourceRef(s+1, len-1, &package, &type, &name,
&attr16, defPackage, &errorMsg)) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, errorMsg);
}
return false;
}
//printf("Pkg: %s, Type: %s, Name: %s\n",
// String8(package).string(), String8(type).string(),
// String8(name).string());
uint32_t specFlags = 0;
uint32_t rid =
identifierForName(name.string(), name.size(),
type.string(), type.size(),
package.string(), package.size(), &specFlags);
if (rid != 0) {
if (enforcePrivate) {
if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Attribute is not public.");
}
return false;
}
}
if (!accessor) {
outValue->data = rid;
return true;
}
rid = Res_MAKEID(
accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
Res_GETTYPE(rid), Res_GETENTRY(rid));
//printf("Incl %s:%s/%s: 0x%08x\n",
// String8(package).string(), String8(type).string(),
// String8(name).string(), rid);
outValue->data = rid;
return true;
}
if (accessor) {
uint32_t rid = accessor->getCustomResource(package, type, name);
if (rid != 0) {
//printf("Mine %s:%s/%s: 0x%08x\n",
// String8(package).string(), String8(type).string(),
// String8(name).string(), rid);
outValue->data = rid;
return true;
}
}
if (accessor != NULL) {
accessor->reportError(accessorCookie, "No resource found that matches the given name");
}
return false;
}
if (stringToInt(s, len, outValue)) {
if ((attrType&ResTable_map::TYPE_INTEGER) == 0) {
// If this type does not allow integers, but does allow floats,
// fall through on this error case because the float type should
// be able to accept any integer value.
if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Integer types not allowed");
}
return false;
}
} else {
if (((int32_t)outValue->data) < ((int32_t)attrMin)
|| ((int32_t)outValue->data) > ((int32_t)attrMax)) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Integer value out of range");
}
return false;
}
return true;
}
}
if (stringToFloat(s, len, outValue)) {
if (outValue->dataType == Res_value::TYPE_DIMENSION) {
if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) {
return true;
}
if (!canStringCoerce) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Dimension types not allowed");
}
return false;
}
} else if (outValue->dataType == Res_value::TYPE_FRACTION) {
if ((attrType&ResTable_map::TYPE_FRACTION) != 0) {
return true;
}
if (!canStringCoerce) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Fraction types not allowed");
}
return false;
}
} else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) {
if (!canStringCoerce) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Float types not allowed");
}
return false;
}
} else {
return true;
}
}
if (len == 4) {
if ((s[0] == 't' || s[0] == 'T') &&
(s[1] == 'r' || s[1] == 'R') &&
(s[2] == 'u' || s[2] == 'U') &&
(s[3] == 'e' || s[3] == 'E')) {
if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
if (!canStringCoerce) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Boolean types not allowed");
}
return false;
}
} else {
outValue->dataType = outValue->TYPE_INT_BOOLEAN;
outValue->data = (uint32_t)-1;
return true;
}
}
}
if (len == 5) {
if ((s[0] == 'f' || s[0] == 'F') &&
(s[1] == 'a' || s[1] == 'A') &&
(s[2] == 'l' || s[2] == 'L') &&
(s[3] == 's' || s[3] == 'S') &&
(s[4] == 'e' || s[4] == 'E')) {
if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
if (!canStringCoerce) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "Boolean types not allowed");
}
return false;
}
} else {
outValue->dataType = outValue->TYPE_INT_BOOLEAN;
outValue->data = 0;
return true;
}
}
}
if ((attrType&ResTable_map::TYPE_ENUM) != 0) {
const ssize_t p = getResourcePackageIndex(attrID);
const bag_entry* bag;
ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
//printf("Got %d for enum\n", cnt);
if (cnt >= 0) {
resource_name rname;
while (cnt > 0) {
if (!Res_INTERNALID(bag->map.name.ident)) {
//printf("Trying attr #%08x\n", bag->map.name.ident);
if (getResourceName(bag->map.name.ident, false, &rname)) {
#if 0
printf("Matching %s against %s (0x%08x)\n",
String8(s, len).string(),
String8(rname.name, rname.nameLen).string(),
bag->map.name.ident);
#endif
if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) {
outValue->dataType = bag->map.value.dataType;
outValue->data = bag->map.value.data;
unlockBag(bag);
return true;
}
}
}
bag++;
cnt--;
}
unlockBag(bag);
}
if (fromAccessor) {
if (accessor->getAttributeEnum(attrID, s, len, outValue)) {
return true;
}
}
}
if ((attrType&ResTable_map::TYPE_FLAGS) != 0) {
const ssize_t p = getResourcePackageIndex(attrID);
const bag_entry* bag;
ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
//printf("Got %d for flags\n", cnt);
if (cnt >= 0) {
bool failed = false;
resource_name rname;
outValue->dataType = Res_value::TYPE_INT_HEX;
outValue->data = 0;
const char16_t* end = s + len;
const char16_t* pos = s;
while (pos < end && !failed) {
const char16_t* start = pos;
pos++;
while (pos < end && *pos != '|') {
pos++;
}
//printf("Looking for: %s\n", String8(start, pos-start).string());
const bag_entry* bagi = bag;
ssize_t i;
for (i=0; i<cnt; i++, bagi++) {
if (!Res_INTERNALID(bagi->map.name.ident)) {
//printf("Trying attr #%08x\n", bagi->map.name.ident);
if (getResourceName(bagi->map.name.ident, false, &rname)) {
#if 0
printf("Matching %s against %s (0x%08x)\n",
String8(start,pos-start).string(),
String8(rname.name, rname.nameLen).string(),
bagi->map.name.ident);
#endif
if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) {
outValue->data |= bagi->map.value.data;
break;
}
}
}
}
if (i >= cnt) {
// Didn't find this flag identifier.
failed = true;
}
if (pos < end) {
pos++;
}
}
unlockBag(bag);
if (!failed) {
//printf("Final flag value: 0x%lx\n", outValue->data);
return true;
}
}
if (fromAccessor) {
if (accessor->getAttributeFlags(attrID, s, len, outValue)) {
//printf("Final flag value: 0x%lx\n", outValue->data);
return true;
}
}
}
if ((attrType&ResTable_map::TYPE_STRING) == 0) {
if (accessor != NULL) {
accessor->reportError(accessorCookie, "String types not allowed");
}
return false;
}
// Generic string handling...
outValue->dataType = outValue->TYPE_STRING;
if (outString) {
bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg);
if (accessor != NULL) {
accessor->reportError(accessorCookie, errorMsg);
}
return failed;
}
return true;
}