com.ibm.icu.text.PluralSamples Maven / Gradle / Ivy
// © 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;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy