All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.tools.lint.checks.PluralsDatabase Maven / Gradle / Ivy

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.tools.lint.checks;

import static com.android.tools.lint.checks.PluralsDatabase.Quantity.few;
import static com.android.tools.lint.checks.PluralsDatabase.Quantity.many;
import static com.android.tools.lint.checks.PluralsDatabase.Quantity.one;
import static com.android.tools.lint.checks.PluralsDatabase.Quantity.two;
import static com.android.tools.lint.checks.PluralsDatabase.Quantity.zero;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.LintUtils;
import com.google.common.collect.Maps;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;

/**
 * Database used by the {@link com.android.tools.lint.checks.PluralsDetector} to get information
 * about plural forms for a given language
 */
public class PluralsDatabase {
    private static final EnumSet NONE = EnumSet.noneOf(Quantity.class);

    private static final PluralsDatabase sInstance = new PluralsDatabase();
    private Map> mPlurals = Maps.newHashMap();

    /** Bit set if this language uses quantity zero */
    @SuppressWarnings("PointlessBitwiseExpression")
    static final int FLAG_ZERO  = 1 << 0;
    /** Bit set if this language uses quantity one */
    static final int FLAG_ONE   = 1 << 1;
    /** Bit set if this language uses quantity two */
    static final int FLAG_TWO   = 1 << 2;
    /** Bit set if this language uses quantity few */
    static final int FLAG_FEW   = 1 << 3;
    /** Bit set if this language uses quantity many */
    static final int FLAG_MANY  = 1 << 4;
    /** Bit set if this language has multiple values that match quantity zero */
    static final int FLAG_MULTIPLE_ZERO = 1 << 5;
    /** Bit set if this language has multiple values that match quantity one */
    static final int FLAG_MULTIPLE_ONE  = 1 << 6;
    /** Bit set if this language has multiple values that match quantity two */
    static final int FLAG_MULTIPLE_TWO  = 1 << 7;

    @NonNull
    public static PluralsDatabase get() {
        return sInstance;
    }

    private static int getFlags(@NonNull String language) {
        int index = getLanguageIndex(language);
        if (index != -1) {
            return FLAGS[index];
        }
        return 0;
    }

    private static int getLanguageIndex(@NonNull String language) {
        int index = Arrays.binarySearch(LANGUAGE_CODES, language);
        if (index >= 0) {
            assert LANGUAGE_CODES[index].equals(language);
            return index;
        } else {
            return -1;
        }
    }

    @Nullable
    public EnumSet getRelevant(@NonNull String language) {
        EnumSet set = mPlurals.get(language);
        if (set == null) {
            int index = getLanguageIndex(language);
            if (index == -1) {
                mPlurals.put(language, NONE);
                return null;
            }

            // Process each item and look for relevance
            int flag = FLAGS[index];

            set = EnumSet.noneOf(Quantity.class);
            if ((flag & FLAG_ZERO) != 0) {
                set.add(zero);
            }
            if ((flag & FLAG_ONE) != 0) {
                set.add(one);
            }
            if ((flag & FLAG_TWO) != 0) {
                set.add(two);
            }
            if ((flag & FLAG_FEW) != 0) {
                set.add(few);
            }
            if ((flag & FLAG_MANY) != 0) {
                set.add(many);
            }

            mPlurals.put(language, set);
        }
        return set == NONE ? null : set;
    }

    @SuppressWarnings("MethodMayBeStatic")
    public boolean hasMultipleValuesForQuantity(
            @NonNull String language,
            @NonNull Quantity quantity) {
        if (quantity == one) {
            return (getFlags(language) & FLAG_MULTIPLE_ONE) != 0;
        } else if (quantity == two) {
            return (getFlags(language) & FLAG_MULTIPLE_TWO) != 0;
        } else {
            return quantity == zero && (getFlags(language) & FLAG_MULTIPLE_ZERO) != 0;
        }
    }

    @SuppressWarnings("MethodMayBeStatic")
    @Nullable
    public String findIntegerExamples(@NonNull String language, @NonNull Quantity quantity) {
        if (quantity == one) {
            return getExampleForQuantityOne(language);
        } else if (quantity == two) {
            return getExampleForQuantityTwo(language);
        } else if (quantity == zero) {
            return getExampleForQuantityZero(language);
        } else {
            return null;
        }
    }

    public enum Quantity {
        // deliberately lower case to match attribute names
        few, many, one, two, zero, other;

        @Nullable
        public static Quantity get(@NonNull String name) {
            for (Quantity quantity : values()) {
                if (name.equals(quantity.name())) {
                    return quantity;
                }
            }

            return null;
        }

        public static String formatSet(@NonNull EnumSet set) {
            List list = new ArrayList(set.size());
            for (Quantity quantity : set) {
                list.add('`' + quantity.name() + '`');
            }
            return LintUtils.formatList(list, Integer.MAX_VALUE);
        }
    }

    // GENERATED DATA.
    // This data is generated by the #testDatabaseAccurate method in PluralsDatabaseTest
    // which will generate the following if it can find an ICU plurals database file
    // in the unit test data folder.

    /** Set of language codes relevant to plurals data */
    private static final String[] LANGUAGE_CODES = new String[] {
            "af", "ak", "am", "ar", "az", "be", "bg", "bh", "bm", "bn",
            "bo", "br", "bs", "ca", "cs", "cy", "da", "de", "dv", "dz",
            "ee", "el", "en", "eo", "es", "et", "eu", "fa", "ff", "fi",
            "fo", "fr", "fy", "ga", "gd", "gl", "gu", "gv", "ha", "he",
            "hi", "hr", "hu", "hy", "id", "ig", "ii", "in", "is", "it",
            "iu", "iw", "ja", "ji", "jv", "ka", "kk", "kl", "km", "kn",
            "ko", "ks", "ku", "kw", "ky", "lb", "lg", "ln", "lo", "lt",
            "lv", "mg", "mk", "ml", "mn", "mr", "ms", "mt", "my", "nb",
            "nd", "ne", "nl", "nn", "no", "nr", "ny", "om", "or", "os",
            "pa", "pl", "ps", "pt", "rm", "ro", "ru", "se", "sg", "si",
            "sk", "sl", "sn", "so", "sq", "sr", "ss", "st", "sv", "sw",
            "ta", "te", "th", "ti", "tk", "tl", "tn", "to", "tr", "ts",
            "ug", "uk", "ur", "uz", "ve", "vi", "vo", "wa", "wo", "xh",
            "yi", "yo", "zh", "zu"
    };

    /**
     * Relevant flags for each language (corresponding to each language listed
     * in the same position in {@link #LANGUAGE_CODES}).
     */
    private static final int[] FLAGS = new int[] {
            0x0002, 0x0042, 0x0042, 0x001f, 0x0002, 0x005a, 0x0002, 0x0042,
            0x0000, 0x0042, 0x0000, 0x00de, 0x004a, 0x0002, 0x000a, 0x001f,
            0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002,
            0x0002, 0x0002, 0x0002, 0x0042, 0x0042, 0x0002, 0x0002, 0x0042,
            0x0002, 0x001e, 0x00ce, 0x0002, 0x0042, 0x00ce, 0x0002, 0x0016,
            0x0042, 0x004a, 0x0002, 0x0042, 0x0000, 0x0000, 0x0000, 0x0000,
            0x0042, 0x0002, 0x0006, 0x0016, 0x0000, 0x0002, 0x0000, 0x0002,
            0x0002, 0x0002, 0x0000, 0x0042, 0x0000, 0x0002, 0x0002, 0x0006,
            0x0002, 0x0002, 0x0002, 0x0042, 0x0000, 0x004a, 0x0063, 0x0042,
            0x0042, 0x0002, 0x0002, 0x0042, 0x0000, 0x001a, 0x0000, 0x0002,
            0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
            0x0002, 0x0002, 0x0042, 0x001a, 0x0002, 0x0002, 0x0002, 0x000a,
            0x005a, 0x0006, 0x0000, 0x0042, 0x000a, 0x00ce, 0x0002, 0x0002,
            0x0002, 0x004a, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
            0x0000, 0x0042, 0x0002, 0x0042, 0x0002, 0x0000, 0x0002, 0x0002,
            0x0002, 0x005a, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0042,
            0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0042
    };

    @Nullable
    private static String getExampleForQuantityZero(@NonNull String language) {
        int index = getLanguageIndex(language);
        switch (index) {
            // set14
            case 70: // lv
                return "0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, \u2026";
            case -1:
            default:
                return null;
        }
    }

    @Nullable
    private static String getExampleForQuantityOne(@NonNull String language) {
        int index = getLanguageIndex(language);
        switch (index) {
            // set1
            case 2: // am
            case 9: // bn
            case 27: // fa
            case 36: // gu
            case 40: // hi
            case 59: // kn
            case 75: // mr
            case 133: // zu
                return "0, 1";
            // set11
            case 48: // is
                return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026";
            // set12
            case 72: // mk
                return "1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, \u2026";
            // set13
            case 115: // tl
                return "0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, \u2026";
            // set14
            case 70: // lv
                return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026";
            // set2
            case 28: // ff
            case 31: // fr
            case 43: // hy
                return "0, 1";
            // set20
            case 12: // bs
            case 41: // hr
            case 105: // sr
                return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026";
            // set21
            case 34: // gd
                return "1, 11";
            // set22
            case 101: // sl
                return "1, 101, 201, 301, 401, 501, 601, 701, 1001, \u2026";
            // set26
            case 5: // be
                return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026";
            // set27
            case 69: // lt
                return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026";
            // set29
            case 96: // ru
            case 121: // uk
                return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026";
            // set30
            case 11: // br
                return "1, 21, 31, 41, 51, 61, 81, 101, 1001, \u2026";
            // set32
            case 37: // gv
                return "1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, \u2026";
            // set5
            case 99: // si
                return "0, 1";
            // set6
            case 1: // ak
            case 7: // bh
            case 67: // ln
            case 71: // mg
            case 90: // pa
            case 113: // ti
            case 127: // wa
                return "0, 1";
            case -1:
            default:
                return null;
        }
    }

    @Nullable
    private static String getExampleForQuantityTwo(@NonNull String language) {
        int index = getLanguageIndex(language);
        switch (index) {
            // set21
            case 34: // gd
                return "2, 12";
            // set22
            case 101: // sl
                return "2, 102, 202, 302, 402, 502, 602, 702, 1002, \u2026";
            // set30
            case 11: // br
                return "2, 22, 32, 42, 52, 62, 82, 102, 1002, \u2026";
            // set32
            case 37: // gv
                return "2, 12, 22, 32, 42, 52, 62, 72, 102, 1002, \u2026";
            case -1:
            default:
                return null;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy