intl/unicharutil/util/ICUUtils.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #ifdef MOZILLA_INTERNAL_API
michael@0 6 #ifdef ENABLE_INTL_API
michael@0 7
michael@0 8 #include "ICUUtils.h"
michael@0 9 #include "mozilla/Preferences.h"
michael@0 10 #include "nsIContent.h"
michael@0 11 #include "nsIDocument.h"
michael@0 12 #include "nsIToolkitChromeRegistry.h"
michael@0 13 #include "nsStringGlue.h"
michael@0 14 #include "unicode/uloc.h"
michael@0 15 #include "unicode/unum.h"
michael@0 16
michael@0 17 using namespace mozilla;
michael@0 18
michael@0 19 /**
michael@0 20 * This pref just controls whether we format the number with grouping separator
michael@0 21 * characters when the internal value is set or updated. It does not stop the
michael@0 22 * user from typing in a number and using grouping separators.
michael@0 23 */
michael@0 24 static bool gLocaleNumberGroupingEnabled;
michael@0 25 static const char LOCALE_NUMBER_GROUPING_PREF_STR[] = "dom.forms.number.grouping";
michael@0 26
michael@0 27 static bool
michael@0 28 LocaleNumberGroupingIsEnabled()
michael@0 29 {
michael@0 30 static bool sInitialized = false;
michael@0 31
michael@0 32 if (!sInitialized) {
michael@0 33 /* check and register ourselves with the pref */
michael@0 34 Preferences::AddBoolVarCache(&gLocaleNumberGroupingEnabled,
michael@0 35 LOCALE_NUMBER_GROUPING_PREF_STR,
michael@0 36 false);
michael@0 37 sInitialized = true;
michael@0 38 }
michael@0 39
michael@0 40 return gLocaleNumberGroupingEnabled;
michael@0 41 }
michael@0 42
michael@0 43 void
michael@0 44 ICUUtils::LanguageTagIterForContent::GetNext(nsACString& aBCP47LangTag)
michael@0 45 {
michael@0 46 if (mCurrentFallbackIndex < 0) {
michael@0 47 mCurrentFallbackIndex = 0;
michael@0 48 // Try the language specified by a 'lang'/'xml:lang' attribute on mContent
michael@0 49 // or any ancestor, if such an attribute is specified:
michael@0 50 nsAutoString lang;
michael@0 51 mContent->GetLang(lang);
michael@0 52 if (!lang.IsEmpty()) {
michael@0 53 aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
michael@0 54 return;
michael@0 55 }
michael@0 56 }
michael@0 57
michael@0 58 if (mCurrentFallbackIndex < 1) {
michael@0 59 mCurrentFallbackIndex = 1;
michael@0 60 // Else try the language specified by any Content-Language HTTP header or
michael@0 61 // pragma directive:
michael@0 62 nsIDocument* doc = mContent->OwnerDoc();
michael@0 63 nsAutoString lang;
michael@0 64 doc->GetContentLanguage(lang);
michael@0 65 if (!lang.IsEmpty()) {
michael@0 66 aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
michael@0 67 return;
michael@0 68 }
michael@0 69 }
michael@0 70
michael@0 71 if (mCurrentFallbackIndex < 2) {
michael@0 72 mCurrentFallbackIndex = 2;
michael@0 73 // Else try the user-agent's locale:
michael@0 74 nsCOMPtr<nsIToolkitChromeRegistry> cr =
michael@0 75 mozilla::services::GetToolkitChromeRegistryService();
michael@0 76 nsAutoCString uaLangTag;
michael@0 77 if (cr) {
michael@0 78 cr->GetSelectedLocale(NS_LITERAL_CSTRING("global"), uaLangTag);
michael@0 79 }
michael@0 80 if (!uaLangTag.IsEmpty()) {
michael@0 81 aBCP47LangTag = uaLangTag;
michael@0 82 return;
michael@0 83 }
michael@0 84 }
michael@0 85
michael@0 86 // TODO: Probably not worth it, but maybe have a fourth fallback to using
michael@0 87 // the OS locale?
michael@0 88
michael@0 89 aBCP47LangTag.Truncate(); // Signal iterator exhausted
michael@0 90 }
michael@0 91
michael@0 92 /* static */ bool
michael@0 93 ICUUtils::LocalizeNumber(double aValue,
michael@0 94 LanguageTagIterForContent& aLangTags,
michael@0 95 nsAString& aLocalizedValue)
michael@0 96 {
michael@0 97 MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
michael@0 98
michael@0 99 static const int32_t kBufferSize = 256;
michael@0 100
michael@0 101 UChar buffer[kBufferSize];
michael@0 102
michael@0 103 nsAutoCString langTag;
michael@0 104 aLangTags.GetNext(langTag);
michael@0 105 while (!langTag.IsEmpty()) {
michael@0 106 UErrorCode status = U_ZERO_ERROR;
michael@0 107 AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
michael@0 108 langTag.get(), nullptr, &status));
michael@0 109 unum_setAttribute(format, UNUM_GROUPING_USED,
michael@0 110 LocaleNumberGroupingIsEnabled());
michael@0 111 // ICU default is a maximum of 3 significant fractional digits. We don't
michael@0 112 // want that limit, so we set it to the maximum that a double can represent
michael@0 113 // (14-16 decimal fractional digits).
michael@0 114 unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, 16);
michael@0 115 int32_t length = unum_formatDouble(format, aValue, buffer, kBufferSize,
michael@0 116 nullptr, &status);
michael@0 117 NS_ASSERTION(length < kBufferSize &&
michael@0 118 status != U_BUFFER_OVERFLOW_ERROR &&
michael@0 119 status != U_STRING_NOT_TERMINATED_WARNING,
michael@0 120 "Need a bigger buffer?!");
michael@0 121 if (U_SUCCESS(status)) {
michael@0 122 ICUUtils::AssignUCharArrayToString(buffer, length, aLocalizedValue);
michael@0 123 return true;
michael@0 124 }
michael@0 125 aLangTags.GetNext(langTag);
michael@0 126 }
michael@0 127 return false;
michael@0 128 }
michael@0 129
michael@0 130 /* static */ double
michael@0 131 ICUUtils::ParseNumber(nsAString& aValue,
michael@0 132 LanguageTagIterForContent& aLangTags)
michael@0 133 {
michael@0 134 MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
michael@0 135
michael@0 136 if (aValue.IsEmpty()) {
michael@0 137 return std::numeric_limits<float>::quiet_NaN();
michael@0 138 }
michael@0 139
michael@0 140 uint32_t length = aValue.Length();
michael@0 141
michael@0 142 nsAutoCString langTag;
michael@0 143 aLangTags.GetNext(langTag);
michael@0 144 while (!langTag.IsEmpty()) {
michael@0 145 UErrorCode status = U_ZERO_ERROR;
michael@0 146 AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
michael@0 147 langTag.get(), nullptr, &status));
michael@0 148 int32_t parsePos = 0;
michael@0 149 static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
michael@0 150 "Unexpected character size - the following cast is unsafe");
michael@0 151 double val = unum_parseDouble(format,
michael@0 152 (const UChar*)PromiseFlatString(aValue).get(),
michael@0 153 length, &parsePos, &status);
michael@0 154 if (U_SUCCESS(status) && parsePos == (int32_t)length) {
michael@0 155 return val;
michael@0 156 }
michael@0 157 aLangTags.GetNext(langTag);
michael@0 158 }
michael@0 159 return std::numeric_limits<float>::quiet_NaN();
michael@0 160 }
michael@0 161
michael@0 162 /* static */ void
michael@0 163 ICUUtils::AssignUCharArrayToString(UChar* aICUString,
michael@0 164 int32_t aLength,
michael@0 165 nsAString& aMozString)
michael@0 166 {
michael@0 167 // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
michael@0 168 // cast here.
michael@0 169
michael@0 170 static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
michael@0 171 "Unexpected character size - the following cast is unsafe");
michael@0 172
michael@0 173 aMozString.Assign((const nsAString::char_type*)aICUString, aLength);
michael@0 174
michael@0 175 NS_ASSERTION((int32_t)aMozString.Length() == aLength, "Conversion failed");
michael@0 176 }
michael@0 177
michael@0 178 #if 0
michael@0 179 /* static */ Locale
michael@0 180 ICUUtils::BCP47CodeToLocale(const nsAString& aBCP47Code)
michael@0 181 {
michael@0 182 MOZ_ASSERT(!aBCP47Code.IsEmpty(), "Don't pass an empty BCP 47 code");
michael@0 183
michael@0 184 Locale locale;
michael@0 185 locale.setToBogus();
michael@0 186
michael@0 187 // BCP47 codes are guaranteed to be ASCII, so lossy conversion is okay
michael@0 188 NS_LossyConvertUTF16toASCII bcp47code(aBCP47Code);
michael@0 189
michael@0 190 UErrorCode status = U_ZERO_ERROR;
michael@0 191 int32_t needed;
michael@0 192
michael@0 193 char localeID[256];
michael@0 194 needed = uloc_forLanguageTag(bcp47code.get(), localeID,
michael@0 195 PR_ARRAY_SIZE(localeID) - 1, nullptr,
michael@0 196 &status);
michael@0 197 MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(localeID)) - 1,
michael@0 198 "Need a bigger buffer");
michael@0 199 if (needed <= 0 || U_FAILURE(status)) {
michael@0 200 return locale;
michael@0 201 }
michael@0 202
michael@0 203 char lang[64];
michael@0 204 needed = uloc_getLanguage(localeID, lang, PR_ARRAY_SIZE(lang) - 1,
michael@0 205 &status);
michael@0 206 MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(lang)) - 1,
michael@0 207 "Need a bigger buffer");
michael@0 208 if (needed <= 0 || U_FAILURE(status)) {
michael@0 209 return locale;
michael@0 210 }
michael@0 211
michael@0 212 char country[64];
michael@0 213 needed = uloc_getCountry(localeID, country, PR_ARRAY_SIZE(country) - 1,
michael@0 214 &status);
michael@0 215 MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(country)) - 1,
michael@0 216 "Need a bigger buffer");
michael@0 217 if (needed > 0 && U_SUCCESS(status)) {
michael@0 218 locale = Locale(lang, country);
michael@0 219 }
michael@0 220
michael@0 221 if (locale.isBogus()) {
michael@0 222 // Using the country resulted in a bogus Locale, so try with only the lang
michael@0 223 locale = Locale(lang);
michael@0 224 }
michael@0 225
michael@0 226 return locale;
michael@0 227 }
michael@0 228
michael@0 229 /* static */ void
michael@0 230 ICUUtils::ToMozString(UnicodeString& aICUString, nsAString& aMozString)
michael@0 231 {
michael@0 232 // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
michael@0 233 // cast here.
michael@0 234
michael@0 235 static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
michael@0 236 "Unexpected character size - the following cast is unsafe");
michael@0 237
michael@0 238 const nsAString::char_type* buf =
michael@0 239 (const nsAString::char_type*)aICUString.getTerminatedBuffer();
michael@0 240 aMozString.Assign(buf);
michael@0 241
michael@0 242 NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
michael@0 243 "Conversion failed");
michael@0 244 }
michael@0 245
michael@0 246 /* static */ void
michael@0 247 ICUUtils::ToICUString(nsAString& aMozString, UnicodeString& aICUString)
michael@0 248 {
michael@0 249 // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
michael@0 250 // cast here.
michael@0 251
michael@0 252 static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
michael@0 253 "Unexpected character size - the following cast is unsafe");
michael@0 254
michael@0 255 aICUString.setTo((UChar*)PromiseFlatString(aMozString).get(),
michael@0 256 aMozString.Length());
michael@0 257
michael@0 258 NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
michael@0 259 "Conversion failed");
michael@0 260 }
michael@0 261 #endif
michael@0 262
michael@0 263 #endif /* ENABLE_INTL_API */
michael@0 264 #endif /* MOZILLA_INTERNAL_API */
michael@0 265

mercurial