bool ResTable::stringToValue()

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;
}