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

com.ibm.icu.text.PluralSamples Maven / Gradle / Ivy

Go to download

International Component for Unicode for Java (ICU4J) is a mature, widely used Java library providing Unicode and Globalization support

There is a newer version: 76.1
Show newest version
/*
 *******************************************************************************
 * Copyright (C) 2013-2014, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package com.ibm.icu.text;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import com.ibm.icu.text.PluralRules.FixedDecimal;
import com.ibm.icu.text.PluralRules.KeywordStatus;
import com.ibm.icu.text.PluralRules.StandardPluralCategories;
import com.ibm.icu.util.Output;

/**
 * @author markdavis
 * Refactor samples as first step to moving into CLDR
 * 
 * @internal
 * @deprecated This API is ICU internal only.
 */
@Deprecated
public class PluralSamples {

    private PluralRules pluralRules;
    private final Map> _keySamplesMap;

    /**
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    public final Map _keyLimitedMap;
    private final Map> _keyFractionSamplesMap;
    private final Set _fractionSamples;

    /**
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    public PluralSamples(PluralRules pluralRules) {
        this.pluralRules = pluralRules;
        Set keywords = pluralRules.getKeywords();
        // ensure both _keySamplesMap and _keyLimitedMap are initialized.
        // If this were allowed to vary on a per-call basis, we'd have to recheck and
        // possibly rebuild the samples cache.  Doesn't seem worth it.
        // This 'max samples' value only applies to keywords that are unlimited, for
        // other keywords all the matching values are returned.  This might be a lot.
        final int MAX_SAMPLES = 3;

        Map temp = new HashMap();
        for (String k : keywords) {
            temp.put(k, pluralRules.isLimited(k));
        }
        _keyLimitedMap = temp;

        Map> sampleMap = new HashMap>();
        int keywordsRemaining = keywords.size();

        int limit = 128; // Math.max(5, getRepeatLimit() * MAX_SAMPLES) * 2;

        for (int i = 0; keywordsRemaining > 0 && i < limit; ++i) {
            keywordsRemaining = addSimpleSamples(pluralRules, MAX_SAMPLES, sampleMap, keywordsRemaining, i / 2.0);
        }
        // Hack for Celtic
        keywordsRemaining = addSimpleSamples(pluralRules, MAX_SAMPLES, sampleMap, keywordsRemaining, 1000000);


        // collect explicit samples
        Map> sampleFractionMap = new HashMap>();
        Set mentioned = new TreeSet();
        // make sure that there is at least one 'other' value
        Map> foundKeywords = new HashMap>();
        for (FixedDecimal s : mentioned) {
            String keyword = pluralRules.select(s);
            addRelation(foundKeywords, keyword, s);
        }
        main:
            if (foundKeywords.size() != keywords.size()) {
                for (int i = 1; i < 1000; ++i) {
                    boolean done = addIfNotPresent(i, mentioned, foundKeywords);
                    if (done) break main;
                }
                // if we are not done, try tenths
                for (int i = 10; i < 1000; ++i) {
                    boolean done = addIfNotPresent(i/10d, mentioned, foundKeywords);
                    if (done) break main;
                }
                System.out.println("Failed to find sample for each keyword: " + foundKeywords + "\n\t" + pluralRules + "\n\t" + mentioned);
            }
        mentioned.add(new FixedDecimal(0)); // always there
        mentioned.add(new FixedDecimal(1)); // always there
        mentioned.add(new FixedDecimal(2)); // always there
        mentioned.add(new FixedDecimal(0.1,1)); // always there
        mentioned.add(new FixedDecimal(1.99,2)); // always there
        mentioned.addAll(fractions(mentioned));
        for (FixedDecimal s : mentioned) {
            String keyword = pluralRules.select(s);
            Set list = sampleFractionMap.get(keyword);
            if (list == null) {
                list = new LinkedHashSet(); // will be sorted because the iteration is
                sampleFractionMap.put(keyword, list);
            }
            list.add(s);
        }

        if (keywordsRemaining > 0) {
            for (String k : keywords) {
                if (!sampleMap.containsKey(k)) {
                    sampleMap.put(k, Collections.emptyList());
                }
                if (!sampleFractionMap.containsKey(k)) {
                    sampleFractionMap.put(k, Collections.emptySet());
                }
            }
        }

        // Make lists immutable so we can return them directly
        for (Entry> entry : sampleMap.entrySet()) {
            sampleMap.put(entry.getKey(), Collections.unmodifiableList(entry.getValue()));
        }
        for (Entry> entry : sampleFractionMap.entrySet()) {
            sampleFractionMap.put(entry.getKey(), Collections.unmodifiableSet(entry.getValue()));
        }
        _keySamplesMap = sampleMap;
        _keyFractionSamplesMap = sampleFractionMap;
        _fractionSamples = Collections.unmodifiableSet(mentioned);
    }

    private int addSimpleSamples(PluralRules pluralRules, final int MAX_SAMPLES, Map> sampleMap,
            int keywordsRemaining, double val) {
        String keyword = pluralRules.select(val);
        boolean keyIsLimited = _keyLimitedMap.get(keyword);

        List list = sampleMap.get(keyword);
        if (list == null) {
            list = new ArrayList(MAX_SAMPLES);
            sampleMap.put(keyword, list);
        } else if (!keyIsLimited && list.size() == MAX_SAMPLES) {
            return keywordsRemaining;
        }
        list.add(Double.valueOf(val));

        if (!keyIsLimited && list.size() == MAX_SAMPLES) {
            --keywordsRemaining;
        }
        return keywordsRemaining;
    }

    private void addRelation(Map> foundKeywords, String keyword, FixedDecimal s) {
        Set set = foundKeywords.get(keyword);
        if (set == null) {
            foundKeywords.put(keyword, set = new HashSet());
        }
        set.add(s);
    }

    private boolean addIfNotPresent(double d, Set mentioned, Map> foundKeywords) {
        FixedDecimal numberInfo = new FixedDecimal(d);
        String keyword = pluralRules.select(numberInfo);
        if (!foundKeywords.containsKey(keyword) || keyword.equals("other")) {
            addRelation(foundKeywords, keyword, numberInfo);
            mentioned.add(numberInfo);
            if (keyword.equals("other")) {
                if (foundKeywords.get("other").size() > 1) {
                    return true;
                }
            }
        }
        return false;
    }

    private static final int[] TENS = {1, 10, 100, 1000, 10000, 100000, 1000000};

    private static final int LIMIT_FRACTION_SAMPLES = 3;


    private Set fractions(Set original) {
        Set toAddTo = new HashSet();

        Set result = new HashSet();
        for (FixedDecimal base1 : original) {
            result.add((int)base1.integerValue);
        }
        List ints = new ArrayList(result);
        Set keywords = new HashSet();

        for (int j = 0; j < ints.size(); ++j) {
            Integer base = ints.get(j);
            String keyword = pluralRules.select(base);
            if (keywords.contains(keyword)) {
                continue;
            }
            keywords.add(keyword);
            toAddTo.add(new FixedDecimal(base,1)); // add .0
            toAddTo.add(new FixedDecimal(base,2)); // add .00
            Integer fract = getDifferentCategory(ints, keyword);
            if (fract >= TENS[LIMIT_FRACTION_SAMPLES-1]) { // make sure that we always get the value
                toAddTo.add(new FixedDecimal(base + "." + fract));
            } else {
                for (int visibleFractions = 1; visibleFractions < LIMIT_FRACTION_SAMPLES; ++visibleFractions) {
                    for (int i = 1; i <= visibleFractions; ++i) {
                        // with visible fractions = 3, and fract = 1, then we should get x.10, 0.01
                        // with visible fractions = 3, and fract = 15, then we should get x.15, x.15
                        if (fract >= TENS[i]) {
                            continue;
                        }
                        toAddTo.add(new FixedDecimal(base + fract/(double)TENS[i], visibleFractions));
                    }
                }
            }
        }
        return toAddTo;
    }

    private Integer getDifferentCategory(List ints, String keyword) {
        for (int i = ints.size() - 1; i >= 0; --i) {
            Integer other = ints.get(i);
            String keywordOther = pluralRules.select(other);
            if (!keywordOther.equals(keyword)) {
                return other;
            }
        }
        return 37;
    }

    @SuppressWarnings("unused")
    private static final Comparator KEYWORD_COMPARATOR = new Comparator () {
        public int compare(String arg0, String arg1) {
            StandardPluralCategories a = StandardPluralCategories.forString(arg0);
            StandardPluralCategories b = StandardPluralCategories.forString(arg1);
            return a == null 
                    ? (b == null ? arg0.compareTo(arg1) : -1)
                            : (b == null ? 1 : a.compareTo(b));
        }
    };

    /**
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    public KeywordStatus getStatus(String keyword, int offset, Set explicits, Output uniqueValue) {
        if (uniqueValue != null) {
            uniqueValue.value = null;
        }

        if (!pluralRules.getKeywords().contains(keyword)) {
            return KeywordStatus.INVALID;
        }
        Collection values = pluralRules.getAllKeywordValues(keyword);
        if (values == null) {
            return KeywordStatus.UNBOUNDED;
        }
        int originalSize = values.size();

        if (explicits == null) {
            explicits = Collections.emptySet();
        }

        // Quick check on whether there are multiple elements

        if (originalSize > explicits.size()) {
            if (originalSize == 1) {
                if (uniqueValue != null) {
                    uniqueValue.value = values.iterator().next();
                }
                return KeywordStatus.UNIQUE;
            }
            return KeywordStatus.BOUNDED;
        }

        // Compute if the quick test is insufficient.

        HashSet subtractedSet = new HashSet(values);
        for (Double explicit : explicits) {
            subtractedSet.remove(explicit - offset);
        }
        if (subtractedSet.size() == 0) {
            return KeywordStatus.SUPPRESSED;
        }

        if (uniqueValue != null && subtractedSet.size() == 1) {
            uniqueValue.value = subtractedSet.iterator().next();
        }

        return originalSize == 1 ? KeywordStatus.UNIQUE : KeywordStatus.BOUNDED;
    }

    Map> getKeySamplesMap() {
        return _keySamplesMap;
    }

    Map> getKeyFractionSamplesMap() {
        return _keyFractionSamplesMap;
    }

    Set getFractionSamples() {
        return _fractionSamples;
    }

    /**
     * Returns all the values that trigger this keyword, or null if the number of such
     * values is unlimited.
     *
     * @param keyword the keyword
     * @return the values that trigger this keyword, or null.  The returned collection
     * is immutable. It will be empty if the keyword is not defined.
     * @stable ICU 4.8
     */

    Collection getAllKeywordValues(String keyword) {
        // HACK for now
        if (!pluralRules.getKeywords().contains(keyword)) {
            return Collections.emptyList();
        }
        Collection result = getKeySamplesMap().get(keyword);

        // We depend on MAX_SAMPLES here.  It's possible for a conjunction
        // of unlimited rules that 'looks' unlimited to return a limited
        // number of values.  There's no bounds to this limited number, in
        // general, because you can construct arbitrarily complex rules.  Since
        // we always generate 3 samples if a rule is really unlimited, that's
        // where we put the cutoff.
        if (result.size() > 2 && !_keyLimitedMap.get(keyword)) {
            return null;
        }
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy