12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others. 22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License 37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/* 47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 5bee65486a185907111f3be60992433e133ec0e32Scott Russell * Copyright (C) 2010-2016, Google, Inc.; International Business Machines * 67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Corporation and others. All Rights Reserved. * 77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.util; 117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collections; 137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Comparator; 147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Iterator; 157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.LinkedHashMap; 167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.LinkedHashSet; 177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map; 187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map.Entry; 197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set; 207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeMap; 217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.regex.Matcher; 227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.regex.Pattern; 237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/** 257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Provides an immutable list of languages (locales) in priority order. 267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The string format is based on the Accept-Language format 27bee65486a185907111f3be60992433e133ec0e32Scott Russell * <a href="http://www.ietf.org/rfc/rfc2616.txt">http://www.ietf.org/rfc/rfc2616.txt</a>, such as 287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "af, en, fr;q=0.9". Syntactically it is slightly 297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * more lenient, in allowing extra whitespace between elements, extra commas, 307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and more than 3 decimals (on input), and pins between 0 and 1. 317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>In theory, Accept-Language indicates the relative 'quality' of each item, 327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * but in practice, all of the browsers just take an ordered list, like 337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "en, fr, de", and synthesize arbitrary quality values that put these in the 347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * right order, like: "en, fr;q=0.7, de;q=0.3". The quality values in these de facto 357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * semantics thus have <b>nothing</b> to do with the relative qualities of the 367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * original. Accept-Language also doesn't 377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * specify the interpretation of multiple instances, eg what "en, fr, en;q=.5" 387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * means. 397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>There are various ways to build a LanguagePriorityList, such 407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * as using the following equivalent patterns: 417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre> 437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * list = LanguagePriorityList.add("af, en, fr;q=0.9").build(); 447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * list2 = LanguagePriorityList 467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * .add(ULocale.forString("af")) 477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * .add(ULocale.ENGLISH) 487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * .add(ULocale.FRENCH, 0.9d) 497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * .build(); 507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre> 517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * When the list is built, the internal values are sorted in descending order by 527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * weight, and then by input order. That is, if two languages have the same weight, the first one in the original order 537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * comes first. If exactly the same language tag appears multiple times, 547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the last one wins. 557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There are two options when building. If preserveWeights are on, then "de;q=0.3, ja;q=0.3, en, fr;q=0.7, de " would result in the following: 577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre> en;q=1.0 587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * de;q=1.0 597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * fr;q=0.7 607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * ja;q=0.3</pre> 617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If it is off (the default), then all weights are reset to 1.0 after reordering. 627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This is to match the effect of the Accept-Language semantics as used in browsers, and results in the following: 637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * * <pre> en;q=1.0 647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * de;q=1.0 657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * fr;q=1.0 667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * ja;q=1.0</pre> 677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author markdavis@google.com 687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class LocalePriorityList implements Iterable<ULocale> { 717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final double D0 = 0.0d; 727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final Double D1 = 1.0d; 737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final Pattern languageSplitter = Pattern.compile("\\s*,\\s*"); 757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final Pattern weightSplitter = Pattern 767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert .compile("\\s*(\\S*)\\s*;\\s*q\\s*=\\s*(\\S*)"); 777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private final Map<ULocale, Double> languagesAndWeights; 787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Add a language code to the list being built, with weight 1.0. 817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param languageCode locale/language to be added 837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return internal builder, for chaining 847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static Builder add(ULocale... languageCode) { 877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return new Builder().add(languageCode); 887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Add a language code to the list being built, with specified weight. 927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param languageCode locale/language to be added 947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param weight value from 0.0 to 1.0 957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return internal builder, for chaining 967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static Builder add(ULocale languageCode, final double weight) { 997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return new Builder().add(languageCode, weight); 1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Add a language priority list. 1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param languagePriorityList list to add all the members of 1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return internal builder, for chaining 1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static Builder add(LocalePriorityList languagePriorityList) { 1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return new Builder().add(languagePriorityList); 1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Add language codes to the list being built, using a string in rfc2616 1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (lenient) format, where each language is a valid {@link ULocale}. 1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param acceptLanguageString String in rfc2616 format (but leniently parsed) 1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return internal builder, for chaining 1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static Builder add(String acceptLanguageString) { 1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return new Builder().add(acceptLanguageString); 1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Return the weight for a given language, or null if there is none. Note that 1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the weights may be adjusted from those used to build the list. 1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param language to get weight of 1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return weight 1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Double getWeight(ULocale language) { 1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return languagesAndWeights.get(language); 1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@inheritDoc} 1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @Override 1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String toString() { 1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final StringBuilder result = new StringBuilder(); 1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (final ULocale language : languagesAndWeights.keySet()) { 1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (result.length() != 0) { 1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert result.append(", "); 1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert result.append(language); 1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert double weight = languagesAndWeights.get(language); 1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (weight != D1) { 1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert result.append(";q=").append(weight); 1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return result.toString(); 1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@inheritDoc} 1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Iterator<ULocale> iterator() { 1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return languagesAndWeights.keySet().iterator(); 1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@inheritDoc} 1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @Override 1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public boolean equals(final Object o) { 1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (o == null) { 1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (this == o) { 1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return true; 1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final LocalePriorityList that = (LocalePriorityList) o; 1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return languagesAndWeights.equals(that.languagesAndWeights); 1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch (final RuntimeException e) { 1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@inheritDoc} 1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @Override 1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int hashCode() { 1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return languagesAndWeights.hashCode(); 1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // ==================== Privates ==================== 1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private LocalePriorityList(final Map<ULocale, Double> languageToWeight) { 1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.languagesAndWeights = languageToWeight; 1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Class used for building LanguagePriorityLists 2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static class Builder { 2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * These store the input languages and weights, in chronological order, 2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * where later additions override previous ones. 2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private final Map<ULocale, Double> languageToWeight 2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert = new LinkedHashMap<ULocale, Double>(); 2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* 2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Private constructor, only used by LocalePriorityList 2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private Builder() { 2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Creates a LocalePriorityList. This is equivalent to 2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@link Builder#build(boolean) Builder.build(false)}. 2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return A LocalePriorityList 2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public LocalePriorityList build() { 2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return build(false); 2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Creates a LocalePriorityList. 2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param preserveWeights when true, the weights originally came 2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * from a language priority list specified by add() are preserved. 2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return A LocalePriorityList 2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public LocalePriorityList build(boolean preserveWeights) { 2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Walk through the input list, collecting the items with the same weights. 2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final Map<Double, Set<ULocale>> doubleCheck = new TreeMap<Double, Set<ULocale>>( 2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert myDescendingDouble); 2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (final ULocale lang : languageToWeight.keySet()) { 2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Double weight = languageToWeight.get(lang); 2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Set<ULocale> s = doubleCheck.get(weight); 2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (s == null) { 2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert doubleCheck.put(weight, s = new LinkedHashSet<ULocale>()); 2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert s.add(lang); 2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // We now have a bunch of items sorted by weight, then chronologically. 2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // We can now create a list in the right order 2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final Map<ULocale, Double> temp = new LinkedHashMap<ULocale, Double>(); 2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (Entry<Double, Set<ULocale>> langEntry : doubleCheck.entrySet()) { 2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final Double weight = langEntry.getKey(); 2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (final ULocale lang : langEntry.getValue()) { 2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert temp.put(lang, preserveWeights ? weight : D1); 2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return new LocalePriorityList(Collections.unmodifiableMap(temp)); 2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Adds a LocalePriorityList 2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param languagePriorityList a LocalePriorityList 2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return this, for chaining 2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Builder add( 2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final LocalePriorityList languagePriorityList) { 2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (final ULocale language : languagePriorityList.languagesAndWeights 2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert .keySet()) { 2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert add(language, languagePriorityList.languagesAndWeights.get(language)); 2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Adds a new language code, with weight = 1.0. 2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param languageCode to add with weight 1.0 2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return this, for chaining 2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Builder add(final ULocale languageCode) { 2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return add(languageCode, D1); 2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Adds language codes, with each having weight = 1.0. 2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param languageCodes List of language codes. 2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return this, for chaining. 2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Builder add(ULocale... languageCodes) { 2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (final ULocale languageCode : languageCodes) { 2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert add(languageCode, D1); 2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Adds a new supported languageCode, with specified weight. Overrides any 3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * previous weight for the language. 3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param languageCode language/locale to add 3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param weight value between 0.0 and 1.1 3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return this, for chaining. 3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Builder add(final ULocale languageCode, 3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert double weight) { 3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (languageToWeight.containsKey(languageCode)) { 3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert languageToWeight.remove(languageCode); 3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (weight <= D0) { 3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; // skip zeros 3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (weight > D1) { 3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert weight = D1; 3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert languageToWeight.put(languageCode, weight); 3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Adds rfc2616 list. 3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param acceptLanguageList in rfc2616 format 3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return this, for chaining. 3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.4 3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Builder add(final String acceptLanguageList) { 3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final String[] items = languageSplitter.split(acceptLanguageList.trim()); 3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final Matcher itemMatcher = weightSplitter.matcher(""); 3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (final String item : items) { 3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (itemMatcher.reset(item).matches()) { 3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final ULocale language = new ULocale(itemMatcher.group(1)); 3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final double weight = Double.parseDouble(itemMatcher.group(2)); 3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!(weight >= D0 && weight <= D1)) { // do ! for NaN 3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException("Illegal weight, must be 0..1: " 3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert + weight); 3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert add(language, weight); 3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (item.length() != 0) { 3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert add(new ULocale(item)); 3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static Comparator<Double> myDescendingDouble = new Comparator<Double>() { 3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int compare(Double o1, Double o2) { 3552d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert int result = o1.compareTo(o2); 3562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert return result > 0 ? -1 : result < 0 ? 1 : 0; // Reverse the order. 3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert }; 3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert} 360