Home | History | Annotate | Download | only in tools
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.inputmethod.keyboard.tools;
     18 
     19 import java.util.HashMap;
     20 import java.util.Locale;
     21 
     22 /**
     23  * A class to help with handling Locales in string form.
     24  *
     25  * This is a subset of com/android/inputmethod/latin/utils/LocaleUtils.java in order to use
     26  * for the make-keyboard-text tool.
     27  */
     28 public final class LocaleUtils {
     29     public static final Locale DEFAULT_LOCALE = Locale.ROOT;
     30     private static final String DEFAULT_LOCALE_CODE = "DEFAULT";
     31     public static final String NO_LANGUAGE_LOCALE_CODE = "zz";
     32     public static final String NO_LANGUAGE_LOCALE_DISPLAY_NAME = "Alphabet";
     33 
     34     private LocaleUtils() {
     35         // Intentional empty constructor for utility class.
     36     }
     37 
     38     private static final HashMap<String, Locale> sLocaleCache = new HashMap<>();
     39 
     40     private static final int INDEX_LANGUAGE = 0;
     41     private static final int INDEX_SCRIPT = 1;
     42     private static final int INDEX_REGION = 2;
     43     private static final int ELEMENT_LIMIT = INDEX_REGION + 1;
     44 
     45     /**
     46      * Creates a locale from a string specification.
     47      *
     48      * Locale string is: language(_script)?(_region)?
     49      * where: language := [a-zA-Z]{2,3}
     50      *        script := [a-zA-Z]{4}
     51      *        region := [a-zA-Z]{2,3}|[0-9]{3}
     52      */
     53     public static Locale constructLocaleFromString(final String localeStr) {
     54         if (localeStr == null) {
     55             return null;
     56         }
     57         synchronized (sLocaleCache) {
     58             if (sLocaleCache.containsKey(localeStr)) {
     59                 return sLocaleCache.get(localeStr);
     60             }
     61             boolean hasRegion = false;
     62             final Locale.Builder builder = new Locale.Builder();
     63             final String[] localeElements = localeStr.split("_", ELEMENT_LIMIT);
     64             if (localeElements.length > INDEX_LANGUAGE) {
     65                 final String text = localeElements[INDEX_LANGUAGE];
     66                 if (isValidLanguage(text)) {
     67                     builder.setLanguage(text);
     68                 } else {
     69                     throw new RuntimeException("Unknown locale format: " + localeStr);
     70                 }
     71             }
     72             if (localeElements.length > INDEX_SCRIPT) {
     73                 final String text = localeElements[INDEX_SCRIPT];
     74                 if (isValidScript(text)) {
     75                     builder.setScript(text);
     76                 } else if (isValidRegion(text)) {
     77                     builder.setRegion(text);
     78                     hasRegion = true;
     79                 } else {
     80                     throw new RuntimeException("Unknown locale format: " + localeStr);
     81                 }
     82             }
     83             if (localeElements.length > INDEX_REGION) {
     84                 final String text = localeElements[INDEX_REGION];
     85                 if (!hasRegion && isValidRegion(text)) {
     86                     builder.setRegion(text);
     87                 } else {
     88                     throw new RuntimeException("Unknown locale format: " + localeStr);
     89                 }
     90             }
     91             final Locale locale = builder.build();
     92             sLocaleCache.put(localeStr, locale);
     93             return locale;
     94         }
     95     }
     96 
     97     private static final int MIN_LENGTH_OF_LANGUAGE = 2;
     98     private static final int MAX_LENGTH_OF_LANGUAGE = 2;
     99     private static final int LENGTH_OF_SCRIPT = 4;
    100     private static final int MIN_LENGTH_OF_REGION = 2;
    101     private static final int MAX_LENGTH_OF_REGION = 2;
    102     private static final int LENGTH_OF_AREA_CODE = 3;
    103 
    104     private static boolean isValidLanguage(final String text) {
    105         return isAlphabetSequence(text, MIN_LENGTH_OF_LANGUAGE, MAX_LENGTH_OF_LANGUAGE);
    106     }
    107 
    108     private static boolean isValidScript(final String text) {
    109         return isAlphabetSequence(text, LENGTH_OF_SCRIPT, LENGTH_OF_SCRIPT);
    110     }
    111 
    112     private static boolean isValidRegion(final String text) {
    113         return isAlphabetSequence(text, MIN_LENGTH_OF_REGION, MAX_LENGTH_OF_REGION)
    114                 || isDigitSequence(text, LENGTH_OF_AREA_CODE, LENGTH_OF_AREA_CODE);
    115     }
    116 
    117     private static boolean isAlphabetSequence(final String text, final int lower, final int upper) {
    118         final int length = text.length();
    119         if (length < lower || length > upper) {
    120             return false;
    121         }
    122         for (int index = 0; index < length; index++) {
    123             if (!isAsciiAlphabet(text.charAt(index))) {
    124                 return false;
    125             }
    126         }
    127         return true;
    128     }
    129 
    130     private static boolean isDigitSequence(final String text, final int lower, final int upper) {
    131         final int length = text.length();
    132         if (length < lower || length > upper) {
    133             return false;
    134         }
    135         for (int index = 0; index < length; ++index) {
    136             if (!isAsciiDigit(text.charAt(index))) {
    137                 return false;
    138             }
    139         }
    140         return true;
    141     }
    142 
    143     private static boolean isAsciiAlphabet(char c) {
    144         return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
    145     }
    146 
    147     private static boolean isAsciiDigit(char c) {
    148         return c >= '0' && c <= '9';
    149     }
    150 
    151     public static String getLocaleCode(final Locale locale) {
    152         if (locale == DEFAULT_LOCALE) {
    153             return DEFAULT_LOCALE_CODE;
    154         }
    155         return locale.toString();
    156     }
    157 
    158     public static String getLocaleDisplayName(final Locale locale) {
    159         if (locale == DEFAULT_LOCALE) {
    160             return DEFAULT_LOCALE_CODE;
    161         }
    162         if (locale.getLanguage().equals(NO_LANGUAGE_LOCALE_CODE)) {
    163             return NO_LANGUAGE_LOCALE_DISPLAY_NAME;
    164         }
    165         return locale.getDisplayName(Locale.ENGLISH);
    166     }
    167 }
    168