Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91447d88 authored by Narayan Kamath's avatar Narayan Kamath
Browse files

Extended locales in AAPT / AssetManager.

Support 3 letter language codes, script codes &
variants. The bulk of the changes are related to
the implementation of command line filtering of
locales etc. The previous code assumed that the
value of each "axis" (locale, density, size etc.)
could be represented by a 4 byte type. This is
no longer the case.

This change introduces a new class, AaptLocaleValue
which holds a (normalized) locale parsed from a
directory name or a filter string. This class takes
responsibility for parsing locales as well as
writing them to ResTable_config structures, which is
their representation in the resource table.

This includes minor changes at the java / JNI level
for AssetManager. We now call locale.toLanguageTag()
to give the native layer a well formed BCP-47 tag.
I've removed some duplicated parsing code in
AssetManager.cpp and replaced them with functions on
ResTable_config. The native getLocales function has
been changed to return well formed BCP-47 locales as
well, so that the corresponding java function can use
Locale.forLanguageTag to construct a Locale object
out of it.

Finally, this change introduces default and copy
constructors for ResTable_config to prevent having
to memset() the associated memory to 0 on every
stack allocation.

Change-Id: I899a56a9a182ee6be52b9389d1ae59266f5482e9
parent 378c6775
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -1625,10 +1625,7 @@ public class Resources {

            String locale = null;
            if (mConfiguration.locale != null) {
                locale = mConfiguration.locale.getLanguage();
                if (mConfiguration.locale.getCountry() != null) {
                    locale += "-" + mConfiguration.locale.getCountry();
                }
                locale = mConfiguration.locale.toLanguageTag();
            }
            int width, height;
            if (mMetrics.widthPixels >= mMetrics.heightPixels) {
+27 −14
Original line number Diff line number Diff line
@@ -1053,7 +1053,7 @@ struct ResTable_config

    // The ISO-15924 short name for the script corresponding to this
    // configuration. (eg. Hant, Latn, etc.). Interpreted in conjunction with
    // the locale field
    // the locale field.
    char localeScript[4];

    // A single BCP-47 variant subtag. Will vary in length between 5 and 8
@@ -1118,14 +1118,23 @@ struct ResTable_config
    bool match(const ResTable_config& settings) const;

    // Get the string representation of the locale component of this
    // Config. This will contain the language along with the prefixed script,
    // region and variant of this config, separated by underscores.
    // Config. The maximum size of this representation will be
    // |RESTABLE_MAX_LOCALE_LEN| (including a terminating '\0').
    //
    // 'r' is the region prefix, 's' is the script prefix and 'v' is the
    // variant prefix.
    //
    // Example: en_rUS, en_sLatn_rUS, en_vPOSIX.
    void getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const;
    // Example: en-US, en-Latn-US, en-POSIX.
    void getBcp47Locale(char* out) const;

    // Sets the values of language, region, script and variant to the
    // well formed BCP-47 locale contained in |in|. The input locale is
    // assumed to be valid and no validation is performed.
    void setBcp47Locale(const char* in);

    inline void clearLocale() {
        locale = 0;
        memset(localeScript, 0, sizeof(localeScript));
        memset(localeVariant, 0, sizeof(localeVariant));
    }

    // Get the 2 or 3 letter language code of this configuration. Trailing
    // bytes are set to '\0'.
    size_t unpackLanguage(char language[4]) const;
@@ -1133,12 +1142,16 @@ struct ResTable_config
    // bytes are set to '\0'.
    size_t unpackRegion(char region[4]) const;

    // Sets the language code of this configuration from |language|. If |language|
    // is a 2 letter code, the trailing byte is expected to be '\0'.
    void packLanguage(const char language[3]);
    // Sets the region code of this configuration from |region|. If |region|
    // is a 2 letter code, the trailing byte is expected to be '\0'.
    void packRegion(const char region[3]);
    // Sets the language code of this configuration to the first three
    // chars at |language|.
    //
    // If |language| is a 2 letter code, the trailing byte must be '\0' or
    // the BCP-47 separator '-'.
    void packLanguage(const char* language);
    // Sets the region code of this configuration to the first three bytes
    // at |region|. If |region| is a 2 letter code, the trailing byte must be '\0'
    // or the BCP-47 separator '-'.
    void packRegion(const char* region);

    // Returns a positive integer if this config is more specific than |o|
    // with respect to their locales, a negative integer if |o| is more specific
+6 −24
Original line number Diff line number Diff line
@@ -463,17 +463,8 @@ void AssetManager::setConfiguration(const ResTable_config& config, const char* l
    if (locale) {
        setLocaleLocked(locale);
    } else if (config.language[0] != 0) {
        char spec[9];
        spec[0] = config.language[0];
        spec[1] = config.language[1];
        if (config.country[0] != 0) {
            spec[2] = '_';
            spec[3] = config.country[0];
            spec[4] = config.country[1];
            spec[5] = 0;
        } else {
            spec[3] = 0;
        }
        char spec[RESTABLE_MAX_LOCALE_LEN];
        config.getBcp47Locale(spec);
        setLocaleLocked(spec);
    } else {
        updateResourceParamsLocked();
@@ -733,20 +724,11 @@ void AssetManager::updateResourceParamsLocked() const
        return;
    }

    size_t llen = mLocale ? strlen(mLocale) : 0;
    mConfig->language[0] = 0;
    mConfig->language[1] = 0;
    mConfig->country[0] = 0;
    mConfig->country[1] = 0;
    if (llen >= 2) {
        mConfig->language[0] = mLocale[0];
        mConfig->language[1] = mLocale[1];
    }
    if (llen >= 5) {
        mConfig->country[0] = mLocale[3];
        mConfig->country[1] = mLocale[4];
    if (mLocale) {
        mConfig->setBcp47Locale(mLocale);
    } else {
        mConfig->clearLocale();
    }
    mConfig->size = sizeof(*mConfig);

    res->setParameters(mConfig);
}
+73 −22
Original line number Diff line number Diff line
@@ -1566,9 +1566,9 @@ void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) {
  return 0;
}

/* static */ void packLanguageOrRegion(const char in[3], const char base,
/* static */ void packLanguageOrRegion(const char* in, const char base,
        char out[2]) {
  if (in[2] == 0) {
  if (in[2] == 0 || in[2] == '-') {
      out[0] = in[0];
      out[1] = in[1];
  } else {
@@ -1582,11 +1582,11 @@ void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) {
}


void ResTable_config::packLanguage(const char language[3]) {
void ResTable_config::packLanguage(const char* language) {
    packLanguageOrRegion(language, 'a', this->language);
}

void ResTable_config::packRegion(const char region[3]) {
void ResTable_config::packRegion(const char* region) {
    packLanguageOrRegion(region, '0', this->country);
}

@@ -2294,7 +2294,7 @@ bool ResTable_config::match(const ResTable_config& settings) const {
    return true;
}

void ResTable_config::getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const {
void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN]) const {
    memset(str, 0, RESTABLE_MAX_LOCALE_LEN);

    // This represents the "any" locale value, which has traditionally been
@@ -2305,34 +2305,83 @@ void ResTable_config::getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const {

    size_t charsWritten = 0;
    if (language[0]) {
        unpackLanguage(str);
        charsWritten += unpackLanguage(str);
    }

    if (country[0]) {
    if (localeScript[0]) {
        if (charsWritten) {
            str[charsWritten++] = '_';
            str[charsWritten++] = 'r';
            str[charsWritten++] = '-';
        }
        charsWritten += unpackRegion(str + charsWritten);
        memcpy(str + charsWritten, localeScript, sizeof(localeScript));
        charsWritten += sizeof(localeScript);
    }

    if (localeScript[0]) {
    if (country[0]) {
        if (charsWritten) {
            str[charsWritten++] = '_';
            str[charsWritten++] = '_s';
            str[charsWritten++] = '-';
        }
        memcpy(str + charsWritten, localeScript, sizeof(localeScript));
        charsWritten += unpackRegion(str + charsWritten);
    }

    if (localeVariant[0]) {
        if (charsWritten) {
            str[charsWritten++] = '_';
            str[charsWritten++] = 'v';
            str[charsWritten++] = '-';
        }
        memcpy(str + charsWritten, localeVariant, sizeof(localeVariant));
    }
}

/* static */ inline bool assignLocaleComponent(ResTable_config* config,
        const char* start, size_t size) {

  switch (size) {
       case 0:
           return false;
       case 2:
       case 3:
           config->language[0] ? config->packRegion(start) : config->packLanguage(start);
           break;
       case 4:
           config->localeScript[0] = toupper(start[0]);
           for (size_t i = 1; i < 4; ++i) {
               config->localeScript[i] = tolower(start[i]);
           }
           break;
       case 5:
       case 6:
       case 7:
       case 8:
           for (size_t i = 0; i < size; ++i) {
               config->localeVariant[i] = tolower(start[i]);
           }
           break;
       default:
           return false;
  }

  return true;
}

void ResTable_config::setBcp47Locale(const char* in) {
    locale = 0;
    memset(localeScript, 0, sizeof(localeScript));
    memset(localeVariant, 0, sizeof(localeVariant));

    const char* separator = in;
    const char* start = in;
    while ((separator = strchr(start, '-')) != NULL) {
        const size_t size = separator - start;
        if (!assignLocaleComponent(this, start, size)) {
            fprintf(stderr, "Invalid BCP-47 locale string: %s", in);
        }

        start = (separator + 1);
    }

    const size_t size = in + strlen(in) - start;
    assignLocaleComponent(this, start, size);
}

String8 ResTable_config::toString() const {
    String8 res;

@@ -2345,7 +2394,7 @@ String8 ResTable_config::toString() const {
        res.appendFormat("%dmnc", dtohs(mnc));
    }
    char localeStr[RESTABLE_MAX_LOCALE_LEN];
    getLocale(localeStr);
    getBcp47Locale(localeStr);
    res.append(localeStr);

    if ((screenLayout&MASK_LAYOUTDIR) != 0) {
@@ -5100,18 +5149,20 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs) const
                const size_t L = type->configs.size();
                for (size_t l=0; l<L; l++) {
                    const ResTable_type* config = type->configs[l];
                    const ResTable_config* cfg = &config->config;
                    ResTable_config cfg;
                    memset(&cfg, 0, sizeof(ResTable_config));
                    cfg.copyFromDtoH(config->config);
                    // only insert unique
                    const size_t M = configs->size();
                    size_t m;
                    for (m=0; m<M; m++) {
                        if (0 == (*configs)[m].compare(*cfg)) {
                        if (0 == (*configs)[m].compare(cfg)) {
                            break;
                        }
                    }
                    // if we didn't find it
                    if (m == M) {
                        configs->add(*cfg);
                        configs->add(cfg);
                    }
                }
            }
@@ -5129,7 +5180,7 @@ void ResTable::getLocales(Vector<String8>* locales) const

    char locale[RESTABLE_MAX_LOCALE_LEN];
    for (size_t i=0; i<I; i++) {
        configs[i].getLocale(locale);
        configs[i].getBcp47Locale(locale);
        const size_t J = locales->size();
        size_t j;
        for (j=0; j<J; j++) {
@@ -5753,7 +5804,7 @@ void ResTable::print(bool inclValues) const
    }
#if 0
    char localeStr[RESTABLE_MAX_LOCALE_LEN];
    mParams.getLocale(localeStr);
    mParams.getBcp47Locale(localeStr);
    printf("mParams=%s,\n" localeStr);
#endif
    size_t pgCount = mPackageGroups.size();
+35 −0
Original line number Diff line number Diff line
@@ -146,5 +146,40 @@ TEST(ResourceTypesTest, IsMoreSpecificThan) {
    EXPECT_TRUE(r.isMoreSpecificThan(l));
}

TEST(ResourceTypesTest, setLocale) {
    ResTable_config test;
    test.setBcp47Locale("en-US");
    EXPECT_EQ('e', test.language[0]);
    EXPECT_EQ('n', test.language[1]);
    EXPECT_EQ('U', test.country[0]);
    EXPECT_EQ('S', test.country[1]);
    EXPECT_EQ(0, test.localeScript[0]);
    EXPECT_EQ(0, test.localeVariant[0]);

    test.setBcp47Locale("eng-419");
    char out[4] = { 1, 1, 1, 1};
    test.unpackLanguage(out);
    EXPECT_EQ('e', out[0]);
    EXPECT_EQ('n', out[1]);
    EXPECT_EQ('g', out[2]);
    EXPECT_EQ(0, out[3]);
    memset(out, 1, 4);
    test.unpackRegion(out);
    EXPECT_EQ('4', out[0]);
    EXPECT_EQ('1', out[1]);
    EXPECT_EQ('9', out[2]);


    test.setBcp47Locale("en-Latn-419");
    memset(out, 1, 4);
    EXPECT_EQ('e', test.language[0]);
    EXPECT_EQ('n', test.language[1]);

    EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
    test.unpackRegion(out);
    EXPECT_EQ('4', out[0]);
    EXPECT_EQ('1', out[1]);
    EXPECT_EQ('9', out[2]);
}

}  // namespace android.
Loading