com.ibm.icu.text.PluralSamples Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
*******************************************************************************
* Copyright (C) 2013-2015, 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.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.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;
}
/**
* @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;
}
}