Home | History | Annotate | Download | only in text
      1 /* GENERATED SOURCE. DO NOT MODIFY. */
      2 //  2016 and later: Unicode, Inc. and others.
      3 // License & terms of use: http://www.unicode.org/copyright.html#License
      4 /*
      5  *******************************************************************************
      6  * Copyright (C) 2013-2015, International Business Machines Corporation and
      7  * others. All Rights Reserved.
      8  *******************************************************************************
      9  */
     10 package android.icu.text;
     11 
     12 import java.util.ArrayList;
     13 import java.util.Collection;
     14 import java.util.Collections;
     15 import java.util.HashMap;
     16 import java.util.HashSet;
     17 import java.util.LinkedHashSet;
     18 import java.util.List;
     19 import java.util.Map;
     20 import java.util.Map.Entry;
     21 import java.util.Set;
     22 import java.util.TreeSet;
     23 
     24 import android.icu.text.PluralRules.FixedDecimal;
     25 import android.icu.text.PluralRules.KeywordStatus;
     26 import android.icu.util.Output;
     27 
     28 /**
     29  * @author markdavis
     30  * Refactor samples as first step to moving into CLDR
     31  *
     32  * @deprecated This API is ICU internal only.
     33  * @hide Only a subset of ICU is exposed in Android
     34  * @hide draft / provisional / internal are hidden on Android
     35  */
     36 @Deprecated
     37 public class PluralSamples {
     38 
     39     private PluralRules pluralRules;
     40     private final Map<String, List<Double>> _keySamplesMap;
     41 
     42     /**
     43      * @deprecated This API is ICU internal only.
     44      * @hide draft / provisional / internal are hidden on Android
     45      */
     46     @Deprecated
     47     public final Map<String, Boolean> _keyLimitedMap;
     48     private final Map<String, Set<FixedDecimal>> _keyFractionSamplesMap;
     49     private final Set<FixedDecimal> _fractionSamples;
     50 
     51     /**
     52      * @deprecated This API is ICU internal only.
     53      * @hide draft / provisional / internal are hidden on Android
     54      */
     55     @Deprecated
     56     public PluralSamples(PluralRules pluralRules) {
     57         this.pluralRules = pluralRules;
     58         Set<String> keywords = pluralRules.getKeywords();
     59         // ensure both _keySamplesMap and _keyLimitedMap are initialized.
     60         // If this were allowed to vary on a per-call basis, we'd have to recheck and
     61         // possibly rebuild the samples cache.  Doesn't seem worth it.
     62         // This 'max samples' value only applies to keywords that are unlimited, for
     63         // other keywords all the matching values are returned.  This might be a lot.
     64         final int MAX_SAMPLES = 3;
     65 
     66         Map<String, Boolean> temp = new HashMap<String, Boolean>();
     67         for (String k : keywords) {
     68             temp.put(k, pluralRules.isLimited(k));
     69         }
     70         _keyLimitedMap = temp;
     71 
     72         Map<String, List<Double>> sampleMap = new HashMap<String, List<Double>>();
     73         int keywordsRemaining = keywords.size();
     74 
     75         int limit = 128; // Math.max(5, getRepeatLimit() * MAX_SAMPLES) * 2;
     76 
     77         for (int i = 0; keywordsRemaining > 0 && i < limit; ++i) {
     78             keywordsRemaining = addSimpleSamples(pluralRules, MAX_SAMPLES, sampleMap, keywordsRemaining, i / 2.0);
     79         }
     80         // Hack for Celtic
     81         keywordsRemaining = addSimpleSamples(pluralRules, MAX_SAMPLES, sampleMap, keywordsRemaining, 1000000);
     82 
     83 
     84         // collect explicit samples
     85         Map<String, Set<FixedDecimal>> sampleFractionMap = new HashMap<String, Set<FixedDecimal>>();
     86         Set<FixedDecimal> mentioned = new TreeSet<FixedDecimal>();
     87         // make sure that there is at least one 'other' value
     88         Map<String, Set<FixedDecimal>> foundKeywords = new HashMap<String, Set<FixedDecimal>>();
     89         for (FixedDecimal s : mentioned) {
     90             String keyword = pluralRules.select(s);
     91             addRelation(foundKeywords, keyword, s);
     92         }
     93         main:
     94             if (foundKeywords.size() != keywords.size()) {
     95                 for (int i = 1; i < 1000; ++i) {
     96                     boolean done = addIfNotPresent(i, mentioned, foundKeywords);
     97                     if (done) break main;
     98                 }
     99                 // if we are not done, try tenths
    100                 for (int i = 10; i < 1000; ++i) {
    101                     boolean done = addIfNotPresent(i/10d, mentioned, foundKeywords);
    102                     if (done) break main;
    103                 }
    104                 System.out.println("Failed to find sample for each keyword: " + foundKeywords + "\n\t" + pluralRules + "\n\t" + mentioned);
    105             }
    106         mentioned.add(new FixedDecimal(0)); // always there
    107         mentioned.add(new FixedDecimal(1)); // always there
    108         mentioned.add(new FixedDecimal(2)); // always there
    109         mentioned.add(new FixedDecimal(0.1,1)); // always there
    110         mentioned.add(new FixedDecimal(1.99,2)); // always there
    111         mentioned.addAll(fractions(mentioned));
    112         for (FixedDecimal s : mentioned) {
    113             String keyword = pluralRules.select(s);
    114             Set<FixedDecimal> list = sampleFractionMap.get(keyword);
    115             if (list == null) {
    116                 list = new LinkedHashSet<FixedDecimal>(); // will be sorted because the iteration is
    117                 sampleFractionMap.put(keyword, list);
    118             }
    119             list.add(s);
    120         }
    121 
    122         if (keywordsRemaining > 0) {
    123             for (String k : keywords) {
    124                 if (!sampleMap.containsKey(k)) {
    125                     sampleMap.put(k, Collections.<Double>emptyList());
    126                 }
    127                 if (!sampleFractionMap.containsKey(k)) {
    128                     sampleFractionMap.put(k, Collections.<FixedDecimal>emptySet());
    129                 }
    130             }
    131         }
    132 
    133         // Make lists immutable so we can return them directly
    134         for (Entry<String, List<Double>> entry : sampleMap.entrySet()) {
    135             sampleMap.put(entry.getKey(), Collections.unmodifiableList(entry.getValue()));
    136         }
    137         for (Entry<String, Set<FixedDecimal>> entry : sampleFractionMap.entrySet()) {
    138             sampleFractionMap.put(entry.getKey(), Collections.unmodifiableSet(entry.getValue()));
    139         }
    140         _keySamplesMap = sampleMap;
    141         _keyFractionSamplesMap = sampleFractionMap;
    142         _fractionSamples = Collections.unmodifiableSet(mentioned);
    143     }
    144 
    145     private int addSimpleSamples(PluralRules pluralRules, final int MAX_SAMPLES, Map<String, List<Double>> sampleMap,
    146             int keywordsRemaining, double val) {
    147         String keyword = pluralRules.select(val);
    148         boolean keyIsLimited = _keyLimitedMap.get(keyword);
    149 
    150         List<Double> list = sampleMap.get(keyword);
    151         if (list == null) {
    152             list = new ArrayList<Double>(MAX_SAMPLES);
    153             sampleMap.put(keyword, list);
    154         } else if (!keyIsLimited && list.size() == MAX_SAMPLES) {
    155             return keywordsRemaining;
    156         }
    157         list.add(Double.valueOf(val));
    158 
    159         if (!keyIsLimited && list.size() == MAX_SAMPLES) {
    160             --keywordsRemaining;
    161         }
    162         return keywordsRemaining;
    163     }
    164 
    165     private void addRelation(Map<String, Set<FixedDecimal>> foundKeywords, String keyword, FixedDecimal s) {
    166         Set<FixedDecimal> set = foundKeywords.get(keyword);
    167         if (set == null) {
    168             foundKeywords.put(keyword, set = new HashSet<FixedDecimal>());
    169         }
    170         set.add(s);
    171     }
    172 
    173     private boolean addIfNotPresent(double d, Set<FixedDecimal> mentioned, Map<String, Set<FixedDecimal>> foundKeywords) {
    174         FixedDecimal numberInfo = new FixedDecimal(d);
    175         String keyword = pluralRules.select(numberInfo);
    176         if (!foundKeywords.containsKey(keyword) || keyword.equals("other")) {
    177             addRelation(foundKeywords, keyword, numberInfo);
    178             mentioned.add(numberInfo);
    179             if (keyword.equals("other")) {
    180                 if (foundKeywords.get("other").size() > 1) {
    181                     return true;
    182                 }
    183             }
    184         }
    185         return false;
    186     }
    187 
    188     private static final int[] TENS = {1, 10, 100, 1000, 10000, 100000, 1000000};
    189 
    190     private static final int LIMIT_FRACTION_SAMPLES = 3;
    191 
    192 
    193     private Set<FixedDecimal> fractions(Set<FixedDecimal> original) {
    194         Set<FixedDecimal> toAddTo = new HashSet<FixedDecimal>();
    195 
    196         Set<Integer> result = new HashSet<Integer>();
    197         for (FixedDecimal base1 : original) {
    198             result.add((int)base1.integerValue);
    199         }
    200         List<Integer> ints = new ArrayList<Integer>(result);
    201         Set<String> keywords = new HashSet<String>();
    202 
    203         for (int j = 0; j < ints.size(); ++j) {
    204             Integer base = ints.get(j);
    205             String keyword = pluralRules.select(base);
    206             if (keywords.contains(keyword)) {
    207                 continue;
    208             }
    209             keywords.add(keyword);
    210             toAddTo.add(new FixedDecimal(base,1)); // add .0
    211             toAddTo.add(new FixedDecimal(base,2)); // add .00
    212             Integer fract = getDifferentCategory(ints, keyword);
    213             if (fract >= TENS[LIMIT_FRACTION_SAMPLES-1]) { // make sure that we always get the value
    214                 toAddTo.add(new FixedDecimal(base + "." + fract));
    215             } else {
    216                 for (int visibleFractions = 1; visibleFractions < LIMIT_FRACTION_SAMPLES; ++visibleFractions) {
    217                     for (int i = 1; i <= visibleFractions; ++i) {
    218                         // with visible fractions = 3, and fract = 1, then we should get x.10, 0.01
    219                         // with visible fractions = 3, and fract = 15, then we should get x.15, x.15
    220                         if (fract >= TENS[i]) {
    221                             continue;
    222                         }
    223                         toAddTo.add(new FixedDecimal(base + fract/(double)TENS[i], visibleFractions));
    224                     }
    225                 }
    226             }
    227         }
    228         return toAddTo;
    229     }
    230 
    231     private Integer getDifferentCategory(List<Integer> ints, String keyword) {
    232         for (int i = ints.size() - 1; i >= 0; --i) {
    233             Integer other = ints.get(i);
    234             String keywordOther = pluralRules.select(other);
    235             if (!keywordOther.equals(keyword)) {
    236                 return other;
    237             }
    238         }
    239         return 37;
    240     }
    241 
    242     /**
    243      * @deprecated This API is ICU internal only.
    244      * @hide draft / provisional / internal are hidden on Android
    245      */
    246     @Deprecated
    247     public KeywordStatus getStatus(String keyword, int offset, Set<Double> explicits, Output<Double> uniqueValue) {
    248         if (uniqueValue != null) {
    249             uniqueValue.value = null;
    250         }
    251 
    252         if (!pluralRules.getKeywords().contains(keyword)) {
    253             return KeywordStatus.INVALID;
    254         }
    255         Collection<Double> values = pluralRules.getAllKeywordValues(keyword);
    256         if (values == null) {
    257             return KeywordStatus.UNBOUNDED;
    258         }
    259         int originalSize = values.size();
    260 
    261         if (explicits == null) {
    262             explicits = Collections.emptySet();
    263         }
    264 
    265         // Quick check on whether there are multiple elements
    266 
    267         if (originalSize > explicits.size()) {
    268             if (originalSize == 1) {
    269                 if (uniqueValue != null) {
    270                     uniqueValue.value = values.iterator().next();
    271                 }
    272                 return KeywordStatus.UNIQUE;
    273             }
    274             return KeywordStatus.BOUNDED;
    275         }
    276 
    277         // Compute if the quick test is insufficient.
    278 
    279         HashSet<Double> subtractedSet = new HashSet<Double>(values);
    280         for (Double explicit : explicits) {
    281             subtractedSet.remove(explicit - offset);
    282         }
    283         if (subtractedSet.size() == 0) {
    284             return KeywordStatus.SUPPRESSED;
    285         }
    286 
    287         if (uniqueValue != null && subtractedSet.size() == 1) {
    288             uniqueValue.value = subtractedSet.iterator().next();
    289         }
    290 
    291         return originalSize == 1 ? KeywordStatus.UNIQUE : KeywordStatus.BOUNDED;
    292     }
    293 
    294     Map<String, List<Double>> getKeySamplesMap() {
    295         return _keySamplesMap;
    296     }
    297 
    298     Map<String, Set<FixedDecimal>> getKeyFractionSamplesMap() {
    299         return _keyFractionSamplesMap;
    300     }
    301 
    302     Set<FixedDecimal> getFractionSamples() {
    303         return _fractionSamples;
    304     }
    305 
    306     /**
    307      * Returns all the values that trigger this keyword, or null if the number of such
    308      * values is unlimited.
    309      *
    310      * @param keyword the keyword
    311      * @return the values that trigger this keyword, or null.  The returned collection
    312      * is immutable. It will be empty if the keyword is not defined.
    313      */
    314 
    315     Collection<Double> getAllKeywordValues(String keyword) {
    316         // HACK for now
    317         if (!pluralRules.getKeywords().contains(keyword)) {
    318             return Collections.<Double>emptyList();
    319         }
    320         Collection<Double> result = getKeySamplesMap().get(keyword);
    321 
    322         // We depend on MAX_SAMPLES here.  It's possible for a conjunction
    323         // of unlimited rules that 'looks' unlimited to return a limited
    324         // number of values.  There's no bounds to this limited number, in
    325         // general, because you can construct arbitrarily complex rules.  Since
    326         // we always generate 3 samples if a rule is really unlimited, that's
    327         // where we put the cutoff.
    328         if (result.size() > 2 && !_keyLimitedMap.get(keyword)) {
    329             return null;
    330         }
    331         return result;
    332     }
    333 }
    334