
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.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 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 PluralsDatabase sInstance = new PluralsDatabase();
private Map> mPlurals;
private Map,Boolean>> mMultiValueSetNames =
Maps.newEnumMap(Quantity.class);
@NonNull
public static PluralsDatabase get() {
return sInstance;
}
@Nullable
public EnumSet getRelevant(@NonNull String language) {
ensureInitialized();
return mPlurals.get(language);
}
public boolean hasMultipleValuesForQuantity(@NonNull String language,
@NonNull Quantity quantity) {
if (quantity == Quantity.one || quantity == Quantity.two || quantity == Quantity.zero) {
ensureInitialized();
EnumSet relevant = mPlurals.get(language);
if (relevant != null) {
Map,Boolean> names = mMultiValueSetNames.get(quantity);
assert names != null : quantity;
return names.containsKey(relevant);
}
}
return false;
}
private void ensureInitialized() {
// Based on the plurals table in plurals.txt in icu4c, version 52:
// external/icu4c/data/misc/plurals.txt
// The format is documented here:
// http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules
if (mPlurals == null) {
initialize();
}
}
@SuppressWarnings({"UnnecessaryLocalVariable", "UnusedDeclaration"})
private void initialize() {
// Quantity.other appears in every single set, so it was instead removed and
// handled at the check site.
EnumSet empty = EnumSet.noneOf(Quantity.class);
EnumSet set0 = EnumSet.of(few, many, one, two, zero);
EnumSet set1 = EnumSet.of(many, one, two);
EnumSet set10 = EnumSet.of(few, many, one);
EnumSet set11 = EnumSet.of(few, one); // "many" are only for fractions
EnumSet set12 = set10;
EnumSet set13 = EnumSet.of(few, one, two);
EnumSet set14 = set10;
EnumSet set15 = EnumSet.of(one);
EnumSet set16 = set0;
EnumSet set17 = EnumSet.of(one, zero);
EnumSet set18 = set11;
EnumSet set19 = EnumSet.of(few, many, one, two);
EnumSet set2 = set15;
EnumSet set20 = set17;
EnumSet set21 = set15;
EnumSet set22 = set13;
EnumSet set23 = set22;
EnumSet set24 = empty;
EnumSet set25 = set15;
EnumSet set26 = set15;
EnumSet set27 = set15;
EnumSet set28 = set15;
EnumSet set29 = set15;
EnumSet set3 = set15;
EnumSet set30 = set15;
EnumSet set31 = set15;
EnumSet set32 = set15;
EnumSet set33 = set18;
EnumSet set34 = EnumSet.of(many, one);
EnumSet set35 = set10;
EnumSet set36 = set15;
EnumSet set37 = set15;
EnumSet set38 = set15;
EnumSet set39 = set13;
EnumSet set4 = set15;
EnumSet set40 = EnumSet.of(many);
EnumSet set41 = set13;
EnumSet set42 = set13;
EnumSet set43 = set19;
EnumSet set44 = set19;
EnumSet set45 = set10;
EnumSet set5 = set17;
EnumSet set6 = EnumSet.of(one, two);
EnumSet set7 = set19;
EnumSet set8 = set18;
EnumSet set9 = set18; // "many" only for fractions, so using different set
// The following sets are used by the mMultiValueSetNames map, and therefore need
// to have their own instances since we will look up set identity
set10 = EnumSet.copyOf(set10);
set13 = EnumSet.copyOf(set13);
set15 = EnumSet.copyOf(set15);
set18 = EnumSet.copyOf(set18);
set19 = EnumSet.copyOf(set19);
set21 = EnumSet.copyOf(set21);
set22 = EnumSet.copyOf(set22);
set23 = EnumSet.copyOf(set23);
set25 = EnumSet.copyOf(set25);
set3 = EnumSet.copyOf(set3);
set30 = EnumSet.copyOf(set30);
set31 = EnumSet.copyOf(set31);
set32 = EnumSet.copyOf(set32);
set33 = EnumSet.copyOf(set33);
set34 = EnumSet.copyOf(set34);
set35 = EnumSet.copyOf(set35);
set38 = EnumSet.copyOf(set38);
set39 = EnumSet.copyOf(set39);
set4 = EnumSet.copyOf(set4);
set40 = EnumSet.copyOf(set40);
set42 = EnumSet.copyOf(set42);
set43 = EnumSet.copyOf(set43);
set44 = EnumSet.copyOf(set44);
set45 = EnumSet.copyOf(set45);
set5 = EnumSet.copyOf(set5);
set9 = EnumSet.copyOf(set9);
final int INITIAL_CAPACITY = 133;
mPlurals = Maps.newHashMapWithExpectedSize(INITIAL_CAPACITY);
mPlurals.put("af", set2);
mPlurals.put("ak", set3);
mPlurals.put("am", set30);
mPlurals.put("ar", set0);
mPlurals.put("az", set2);
mPlurals.put("be", set10);
mPlurals.put("bg", set2);
mPlurals.put("bh", set3);
mPlurals.put("bm", set24);
mPlurals.put("bn", set30);
mPlurals.put("bo", set24);
mPlurals.put("br", set19);
mPlurals.put("bs", set33);
mPlurals.put("ca", set26);
mPlurals.put("cs", set11);
mPlurals.put("cy", set16);
mPlurals.put("da", set28);
mPlurals.put("de", set26);
mPlurals.put("dv", set2);
mPlurals.put("dz", set24);
mPlurals.put("ee", set2);
mPlurals.put("el", set2);
mPlurals.put("en", set26);
mPlurals.put("eo", set2);
mPlurals.put("es", set2);
mPlurals.put("et", set26);
mPlurals.put("eu", set2);
mPlurals.put("fa", set30);
mPlurals.put("ff", set4);
mPlurals.put("fi", set26);
mPlurals.put("fo", set2);
mPlurals.put("fr", set4);
mPlurals.put("fy", set2);
mPlurals.put("ga", set7);
mPlurals.put("gd", set23);
mPlurals.put("gl", set26);
mPlurals.put("gu", set30);
mPlurals.put("gv", set22);
mPlurals.put("ha", set2);
mPlurals.put("he", set1);
mPlurals.put("hi", set30);
mPlurals.put("hr", set33);
mPlurals.put("hu", set2);
mPlurals.put("hy", set4);
mPlurals.put("id", set24);
mPlurals.put("ig", set24);
mPlurals.put("ii", set24);
mPlurals.put("in", set24);
mPlurals.put("is", set31);
mPlurals.put("it", set26);
mPlurals.put("iu", set6);
mPlurals.put("iw", set1);
mPlurals.put("ja", set24);
mPlurals.put("ji", set26);
mPlurals.put("jv", set24);
// Javanese replaced by "jv"
//mPlurals.put("jw", set24);
mPlurals.put("ka", set2);
mPlurals.put("kk", set2);
mPlurals.put("kl", set2);
mPlurals.put("km", set24);
mPlurals.put("kn", set30);
mPlurals.put("ko", set24);
mPlurals.put("ks", set2);
mPlurals.put("ku", set2);
mPlurals.put("kw", set6);
mPlurals.put("ky", set2);
mPlurals.put("lb", set2);
mPlurals.put("lg", set2);
mPlurals.put("ln", set3);
mPlurals.put("lo", set24);
mPlurals.put("lt", set9);
mPlurals.put("lv", set5);
mPlurals.put("mg", set3);
mPlurals.put("mk", set15);
mPlurals.put("ml", set2);
mPlurals.put("mn", set2);
// Deprecated
//mPlurals.put("mo", set8);
mPlurals.put("mr", set30);
mPlurals.put("ms", set24);
mPlurals.put("mt", set14);
mPlurals.put("my", set24);
mPlurals.put("nb", set2);
mPlurals.put("nd", set2);
mPlurals.put("ne", set2);
mPlurals.put("nl", set26);
mPlurals.put("nn", set2);
mPlurals.put("no", set2);
mPlurals.put("nr", set2);
mPlurals.put("ny", set2);
mPlurals.put("om", set2);
mPlurals.put("or", set2);
mPlurals.put("os", set2);
mPlurals.put("pa", set3);
mPlurals.put("pl", set12);
mPlurals.put("ps", set2);
mPlurals.put("pt", set27);
// Luckily these sets are identical so we don't need to make a region distinction
// in the API
//mPlurals.put("pt_PT", set29); // XXX
mPlurals.put("rm", set2);
mPlurals.put("ro", set8);
mPlurals.put("ru", set34);
mPlurals.put("se", set6);
mPlurals.put("sg", set24);
// sh was removed from 639-1 to 639-2
//mPlurals.put("sh", set33);
mPlurals.put("si", set32);
mPlurals.put("sk", set11);
mPlurals.put("sl", set13);
mPlurals.put("sn", set2);
mPlurals.put("so", set2);
mPlurals.put("sq", set2);
mPlurals.put("sr", set33);
mPlurals.put("ss", set2);
mPlurals.put("st", set2);
mPlurals.put("sv", set26);
mPlurals.put("sw", set26);
mPlurals.put("ta", set2);
mPlurals.put("te", set2);
mPlurals.put("th", set24);
mPlurals.put("ti", set3);
mPlurals.put("tk", set2);
mPlurals.put("tl", set25);
mPlurals.put("tn", set2);
mPlurals.put("to", set24);
mPlurals.put("tr", set2);
mPlurals.put("ts", set2);
mPlurals.put("uk", set35);
mPlurals.put("ur", set26);
mPlurals.put("uz", set2);
mPlurals.put("ve", set2);
mPlurals.put("vi", set24);
mPlurals.put("vo", set2);
mPlurals.put("wa", set3);
mPlurals.put("wo", set24);
mPlurals.put("xh", set2);
mPlurals.put("yi", set26);
mPlurals.put("yo", set24);
mPlurals.put("zh", set24);
mPlurals.put("zu", set30);
assert mPlurals.size() == INITIAL_CAPACITY : mPlurals.size();
// Sets where more than a single integer maps to one. Take for example
// set 10:
// set10{
// one{
// "n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81,"
// " 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0"
// ", 101.0, 1001.0, …"
// }
// }
// Here we see that both "1" and "21" will match the "one" category.
// Note that this only applies to integers (since getQuantityString only takes integer)
// whereas the plurals data also covers fractions. I was not sure what to do about
// set17:
// set17{
// one{"i = 0,1 and n != 0 @integer 1 @decimal 0.1~1.6"}
// }
// since it looks to me like this only differs from 1 in the fractional part.
//noinspection unchecked
mMultiValueSetNames.put(Quantity.one, newIdentityHashMap(
set10, set13, set15, set18, set19, set21, set22, set23, set25, set3, set30,
set31, set32, set33, set34, set35, set38, set39, set4, set40, set42, set45,
set5, set9
));
// Sets where more than a single integer maps to two.
//noinspection unchecked
mMultiValueSetNames.put(Quantity.two, newIdentityHashMap(
set13, set19, set22, set23, set40, set43, set44, set45
));
// Sets where more than a single integer maps to zero.
//noinspection unchecked
mMultiValueSetNames.put(Quantity.zero, newIdentityHashMap(set5));
}
private static Map,Boolean> newIdentityHashMap(Set... elements) {
Map,Boolean> map = Maps.newIdentityHashMap();
for (Set set : elements) {
map.put(set, true);
}
return map;
}
@SuppressWarnings({"MethodMayBeStatic", "UnusedParameters"})
@Nullable
public String findIntegerExamples(@NonNull String language, @NonNull Quantity quantity) {
// Need plurals database
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(EnumSet set) {
List list = new ArrayList(set.size());
for (Quantity quantity : set) {
list.add(quantity.name());
}
return LintUtils.formatList(list, Integer.MAX_VALUE);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy