| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html#License |
| /* |
| ******************************************************************************* |
| * |
| * Copyright (C) 1996-2016, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| * |
| ******************************************************************************* |
| * |
| * CollationLoader.java, ported from ucol_res.cpp |
| * |
| * created by: Markus W. Scherer |
| */ |
| |
| package com.ibm.icu.impl.coll; |
| |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.util.MissingResourceException; |
| |
| import com.ibm.icu.impl.ICUData; |
| import com.ibm.icu.impl.ICUResourceBundle; |
| import com.ibm.icu.util.ICUUncheckedIOException; |
| import com.ibm.icu.util.Output; |
| import com.ibm.icu.util.ULocale; |
| import com.ibm.icu.util.UResourceBundle; |
| |
| /** |
| * Convenience string denoting the Collation data tree |
| */ |
| public final class CollationLoader { |
| |
| // not implemented, all methods are static |
| private CollationLoader() { |
| } |
| |
| private static volatile String rootRules = null; |
| |
| private static void loadRootRules() { |
| if (rootRules != null) { |
| return; |
| } |
| synchronized(CollationLoader.class) { |
| if (rootRules == null) { |
| UResourceBundle rootBundle = UResourceBundle.getBundleInstance( |
| ICUData.ICU_COLLATION_BASE_NAME, ULocale.ROOT); |
| rootRules = rootBundle.getString("UCARules"); |
| } |
| } |
| } |
| |
| // C++: static void appendRootRules(UnicodeString &s) |
| public static String getRootRules() { |
| loadRootRules(); |
| return rootRules; |
| } |
| |
| /** |
| * Simpler/faster methods for ASCII than ones based on Unicode data. |
| * TODO: There should be code like this somewhere already?? |
| */ |
| private static final class ASCII { |
| static String toLowerCase(String s) { |
| for (int i = 0; i < s.length(); ++i) { |
| char c = s.charAt(i); |
| if ('A' <= c && c <= 'Z') { |
| StringBuilder sb = new StringBuilder(s.length()); |
| sb.append(s, 0, i).append((char)(c + 0x20)); |
| while (++i < s.length()) { |
| c = s.charAt(i); |
| if ('A' <= c && c <= 'Z') { c = (char)(c + 0x20); } |
| sb.append(c); |
| } |
| return sb.toString(); |
| } |
| } |
| return s; |
| } |
| } |
| |
| static String loadRules(ULocale locale, String collationType) { |
| UResourceBundle bundle = UResourceBundle.getBundleInstance( |
| ICUData.ICU_COLLATION_BASE_NAME, locale); |
| UResourceBundle data = ((ICUResourceBundle)bundle).getWithFallback( |
| "collations/" + ASCII.toLowerCase(collationType)); |
| String rules = data.getString("Sequence"); |
| return rules; |
| } |
| |
| private static final UResourceBundle findWithFallback(UResourceBundle table, String entryName) { |
| return ((ICUResourceBundle)table).findWithFallback(entryName); |
| } |
| |
| public static CollationTailoring loadTailoring(ULocale locale, Output<ULocale> outValidLocale) { |
| |
| // Java porting note: ICU4J getWithFallback/getStringWithFallback currently does not |
| // work well when alias table is involved in a resource path, unless full path is specified. |
| // For now, collation resources does not contain such data, so the code below should work fine. |
| |
| CollationTailoring root = CollationRoot.getRoot(); |
| String localeName = locale.getName(); |
| if (localeName.length() == 0 || localeName.equals("root")) { |
| outValidLocale.value = ULocale.ROOT; |
| return root; |
| } |
| |
| UResourceBundle bundle = null; |
| try { |
| bundle = ICUResourceBundle.getBundleInstance( |
| ICUData.ICU_COLLATION_BASE_NAME, locale, |
| ICUResourceBundle.OpenType.LOCALE_ROOT); |
| } catch (MissingResourceException e) { |
| outValidLocale.value = ULocale.ROOT; |
| return root; |
| } |
| |
| ULocale validLocale = bundle.getULocale(); |
| // Normalize the root locale. See |
| // http://bugs.icu-project.org/trac/ticket/10715 |
| String validLocaleName = validLocale.getName(); |
| if (validLocaleName.length() == 0 || validLocaleName.equals("root")) { |
| validLocale = ULocale.ROOT; |
| } |
| outValidLocale.value = validLocale; |
| |
| // There are zero or more tailorings in the collations table. |
| UResourceBundle collations; |
| try { |
| collations = bundle.get("collations"); |
| if (collations == null) { |
| return root; |
| } |
| } catch(MissingResourceException ignored) { |
| return root; |
| } |
| |
| // Fetch the collation type from the locale ID and the default type from the data. |
| String type = locale.getKeywordValue("collation"); |
| String defaultType = "standard"; |
| |
| String defT = ((ICUResourceBundle)collations).findStringWithFallback("default"); |
| if (defT != null) { |
| defaultType = defT; |
| } |
| |
| if (type == null || type.equals("default")) { |
| type = defaultType; |
| } else { |
| type = ASCII.toLowerCase(type); |
| } |
| |
| // Load the collations/type tailoring, with type fallback. |
| |
| // Java porting note: typeFallback is used for setting U_USING_DEFAULT_WARNING in |
| // ICU4C, but not used by ICU4J |
| |
| // boolean typeFallback = false; |
| UResourceBundle data = findWithFallback(collations, type); |
| if (data == null && |
| type.length() > 6 && type.startsWith("search")) { |
| // fall back from something like "searchjl" to "search" |
| // typeFallback = true; |
| type = "search"; |
| data = findWithFallback(collations, type); |
| } |
| |
| if (data == null && !type.equals(defaultType)) { |
| // fall back to the default type |
| // typeFallback = true; |
| type = defaultType; |
| data = findWithFallback(collations, type); |
| } |
| |
| if (data == null && !type.equals("standard")) { |
| // fall back to the "standard" type |
| // typeFallback = true; |
| type = "standard"; |
| data = findWithFallback(collations, type); |
| } |
| |
| if (data == null) { |
| return root; |
| } |
| |
| // Is this the same as the root collator? If so, then use that instead. |
| ULocale actualLocale = data.getULocale(); |
| // http://bugs.icu-project.org/trac/ticket/10715 ICUResourceBundle(root).getULocale() != ULocale.ROOT |
| // Therefore not just if (actualLocale.equals(ULocale.ROOT) && type.equals("standard")) { |
| String actualLocaleName = actualLocale.getName(); |
| if (actualLocaleName.length() == 0 || actualLocaleName.equals("root")) { |
| actualLocale = ULocale.ROOT; |
| if (type.equals("standard")) { |
| return root; |
| } |
| } |
| |
| CollationTailoring t = new CollationTailoring(root.settings); |
| t.actualLocale = actualLocale; |
| |
| // deserialize |
| UResourceBundle binary = data.get("%%CollationBin"); |
| ByteBuffer inBytes = binary.getBinary(); |
| try { |
| CollationDataReader.read(root, inBytes, t); |
| } catch (IOException e) { |
| throw new ICUUncheckedIOException("Failed to load collation tailoring data for locale:" |
| + actualLocale + " type:" + type, e); |
| } |
| |
| // Try to fetch the optional rules string. |
| try { |
| t.setRulesResource(data.get("Sequence")); |
| } catch(MissingResourceException ignored) { |
| } |
| |
| // Set the collation types on the informational locales, |
| // except when they match the default types (for brevity and backwards compatibility). |
| // For the valid locale, suppress the default type. |
| if (!type.equals(defaultType)) { |
| outValidLocale.value = validLocale.setKeywordValue("collation", type); |
| } |
| |
| // For the actual locale, suppress the default type *according to the actual locale*. |
| // For example, zh has default=pinyin and contains all of the Chinese tailorings. |
| // zh_Hant has default=stroke but has no other data. |
| // For the valid locale "zh_Hant" we need to suppress stroke. |
| // For the actual locale "zh" we need to suppress pinyin instead. |
| if (!actualLocale.equals(validLocale)) { |
| // Opening a bundle for the actual locale should always succeed. |
| UResourceBundle actualBundle = UResourceBundle.getBundleInstance( |
| ICUData.ICU_COLLATION_BASE_NAME, actualLocale); |
| defT = ((ICUResourceBundle)actualBundle).findStringWithFallback("collations/default"); |
| if (defT != null) { |
| defaultType = defT; |
| } |
| } |
| |
| if (!type.equals(defaultType)) { |
| t.actualLocale = t.actualLocale.setKeywordValue("collation", type); |
| } |
| |
| // if (typeFallback) { |
| // ICU4C implementation sets U_USING_DEFAULT_WARNING here |
| // } |
| |
| return t; |
| } |
| } |