/src/libreoffice/unotools/source/i18n/localedatawrapper.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <limits> |
21 | | #include <stdio.h> |
22 | | #include <string> |
23 | | |
24 | | #include <sal/log.hxx> |
25 | | #include <unotools/localedatawrapper.hxx> |
26 | | #include <unotools/digitgroupingiterator.hxx> |
27 | | #include <comphelper/diagnose_ex.hxx> |
28 | | #include <tools/debug.hxx> |
29 | | #include <i18nlangtag/languagetag.hxx> |
30 | | #include <o3tl/safeint.hxx> |
31 | | |
32 | | #include <com/sun/star/i18n/KNumberFormatUsage.hpp> |
33 | | #include <com/sun/star/i18n/KNumberFormatType.hpp> |
34 | | #include <com/sun/star/i18n/LocaleData2.hpp> |
35 | | #include <com/sun/star/i18n/NumberFormatIndex.hpp> |
36 | | #include <com/sun/star/i18n/NumberFormatMapper.hpp> |
37 | | |
38 | | #include <comphelper/processfactory.hxx> |
39 | | #include <comphelper/sequence.hxx> |
40 | | #include <rtl/ustrbuf.hxx> |
41 | | #include <rtl/math.hxx> |
42 | | #include <tools/date.hxx> |
43 | | #include <tools/time.hxx> |
44 | | #include <tools/duration.hxx> |
45 | | #include <o3tl/string_view.hxx> |
46 | | #include <map> |
47 | | #include <mutex> |
48 | | #include <utility> |
49 | | |
50 | | const sal_uInt16 nCurrFormatDefault = 0; |
51 | | |
52 | | using namespace ::com::sun::star; |
53 | | using namespace ::com::sun::star::i18n; |
54 | | using namespace ::com::sun::star::uno; |
55 | | |
56 | | namespace |
57 | | { |
58 | | uno::Sequence< lang::Locale > gInstalledLocales; |
59 | | std::vector< LanguageType > gInstalledLanguageTypes; |
60 | | } |
61 | | |
62 | | sal_uInt8 LocaleDataWrapper::nLocaleDataChecking = 0; |
63 | | |
64 | | /** |
65 | | * Loading LocaleDataWrapper can become expensive because of all the function-symbol lookups required, so |
66 | | * we cache these. |
67 | | */ |
68 | | // static |
69 | | const LocaleDataWrapper* LocaleDataWrapper::get(const LanguageTag& aLanguageTag) |
70 | 381k | { |
71 | 381k | static std::map<LanguageTag, std::unique_ptr<LocaleDataWrapper>> gCache; |
72 | 381k | static std::mutex gMutex; |
73 | | |
74 | 381k | std::unique_lock l(gMutex); |
75 | 381k | auto it = gCache.find(aLanguageTag); |
76 | 381k | if (it != gCache.end()) |
77 | 381k | return it->second.get(); |
78 | 821 | auto pNew = new LocaleDataWrapper(comphelper::getProcessComponentContext(), aLanguageTag); |
79 | 821 | gCache.insert({aLanguageTag, std::unique_ptr<LocaleDataWrapper>(pNew)}); |
80 | 821 | return pNew; |
81 | 381k | }; |
82 | | |
83 | | |
84 | | LocaleDataWrapper::LocaleDataWrapper( |
85 | | const Reference< uno::XComponentContext > & rxContext, |
86 | | LanguageTag aLanguageTag |
87 | | ) |
88 | | : |
89 | 835 | m_xContext( rxContext ), |
90 | 835 | xLD( LocaleData2::create(rxContext) ), |
91 | 835 | maLanguageTag(std::move( aLanguageTag )) |
92 | 835 | { |
93 | 835 | loadData(); |
94 | 835 | loadDateAcceptancePatterns({}); |
95 | 835 | } |
96 | | |
97 | | LocaleDataWrapper::LocaleDataWrapper( |
98 | | LanguageTag aLanguageTag, |
99 | | const std::vector<OUString> & rOverrideDateAcceptancePatterns |
100 | | ) |
101 | | : |
102 | 9.17k | m_xContext( comphelper::getProcessComponentContext() ), |
103 | 9.17k | xLD( LocaleData2::create(m_xContext) ), |
104 | 9.17k | maLanguageTag(std::move( aLanguageTag )) |
105 | 9.17k | { |
106 | 9.17k | loadData(); |
107 | 9.17k | loadDateAcceptancePatterns(rOverrideDateAcceptancePatterns); |
108 | 9.17k | } |
109 | | |
110 | | LocaleDataWrapper::~LocaleDataWrapper() |
111 | 8.80k | { |
112 | 8.80k | } |
113 | | |
114 | | const LanguageTag& LocaleDataWrapper::getLanguageTag() const |
115 | 320k | { |
116 | 320k | return maLanguageTag; |
117 | 320k | } |
118 | | |
119 | | const css::lang::Locale& LocaleDataWrapper::getMyLocale() const |
120 | 3.37M | { |
121 | 3.37M | return maLanguageTag.getLocale(); |
122 | 3.37M | } |
123 | | |
124 | | void LocaleDataWrapper::loadData() |
125 | 9.92k | { |
126 | 9.92k | const css::lang::Locale& rMyLocale = maLanguageTag.getLocale(); |
127 | | |
128 | 9.92k | { |
129 | 9.92k | const Sequence< Currency2 > aCurrSeq = getAllCurrencies(); |
130 | 9.92k | if ( !aCurrSeq.hasElements() ) |
131 | 0 | { |
132 | 0 | if (areChecksEnabled()) |
133 | 0 | outputCheckMessage("LocaleDataWrapper::getCurrSymbolsImpl: no currency at all, using ShellsAndPebbles"); |
134 | 0 | aCurrSymbol = "ShellsAndPebbles"; |
135 | 0 | aCurrBankSymbol = aCurrSymbol; |
136 | 0 | nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault; |
137 | 0 | nCurrDigits = 2; |
138 | 0 | } |
139 | 9.92k | else |
140 | 9.92k | { |
141 | 9.92k | auto pCurr = std::find_if(aCurrSeq.begin(), aCurrSeq.end(), |
142 | 9.93k | [](const Currency2& rCurr) { return rCurr.Default; }); |
143 | 9.92k | if ( pCurr == aCurrSeq.end() ) |
144 | 0 | { |
145 | 0 | if (areChecksEnabled()) |
146 | 0 | { |
147 | 0 | outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrSymbolsImpl: no default currency" ) ); |
148 | 0 | } |
149 | 0 | pCurr = aCurrSeq.begin(); |
150 | 0 | } |
151 | 9.92k | aCurrSymbol = pCurr->Symbol; |
152 | 9.92k | aCurrBankSymbol = pCurr->BankSymbol; |
153 | 9.92k | nCurrDigits = pCurr->DecimalPlaces; |
154 | 9.92k | } |
155 | 9.92k | } |
156 | | |
157 | 9.92k | loadCurrencyFormats(); |
158 | | |
159 | 9.92k | { |
160 | 9.92k | xDefaultCalendar.reset(); |
161 | 9.92k | xSecondaryCalendar.reset(); |
162 | 9.92k | const Sequence< Calendar2 > xCals = getAllCalendars(); |
163 | 9.92k | if (xCals.getLength() > 1) |
164 | 8.38k | { |
165 | 8.38k | auto pCal = std::find_if(xCals.begin(), xCals.end(), |
166 | 16.7k | [](const Calendar2& rCal) { return !rCal.Default; }); |
167 | 8.38k | if (pCal != xCals.end()) |
168 | 8.38k | xSecondaryCalendar = std::make_shared<Calendar2>( *pCal); |
169 | 8.38k | } |
170 | 9.92k | auto pCal = xCals.begin(); |
171 | 9.92k | if (xCals.getLength() > 1) |
172 | 8.38k | { |
173 | 8.38k | pCal = std::find_if(xCals.begin(), xCals.end(), |
174 | 8.38k | [](const Calendar2& rCal) { return rCal.Default; }); |
175 | 8.38k | if (pCal == xCals.end()) |
176 | 0 | pCal = xCals.begin(); |
177 | 8.38k | } |
178 | 9.92k | xDefaultCalendar = std::make_shared<Calendar2>( *pCal); |
179 | 9.92k | } |
180 | | |
181 | 9.92k | loadDateOrders(); |
182 | | |
183 | 9.92k | try |
184 | 9.92k | { |
185 | 9.92k | aDateAcceptancePatterns = xLD->getDateAcceptancePatterns( rMyLocale ); |
186 | 9.92k | } |
187 | 9.92k | catch (const Exception&) |
188 | 9.92k | { |
189 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDateAcceptancePatterns" ); |
190 | 0 | aDateAcceptancePatterns = {}; |
191 | 0 | } |
192 | | |
193 | | |
194 | 8.83k | loadDigitGrouping(); |
195 | | |
196 | 8.83k | try |
197 | 8.83k | { |
198 | 8.83k | aReservedWords = comphelper::sequenceToContainer<std::vector<OUString>>(xLD->getReservedWord( rMyLocale )); |
199 | 8.83k | } |
200 | 8.83k | catch ( const Exception& ) |
201 | 8.83k | { |
202 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "getReservedWord" ); |
203 | 0 | } |
204 | | |
205 | 8.83k | try |
206 | 8.83k | { |
207 | 8.83k | aLocaleDataItem = xLD->getLocaleItem2( rMyLocale ); |
208 | 8.83k | } |
209 | 8.83k | catch (const Exception&) |
210 | 8.83k | { |
211 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLocaleItem" ); |
212 | 0 | static const css::i18n::LocaleDataItem2 aEmptyItem; |
213 | 0 | aLocaleDataItem = aEmptyItem; |
214 | 0 | } |
215 | | |
216 | 8.83k | aLocaleItem[LocaleItem::DATE_SEPARATOR] = aLocaleDataItem.dateSeparator; |
217 | 8.83k | aLocaleItem[LocaleItem::THOUSAND_SEPARATOR] = aLocaleDataItem.thousandSeparator; |
218 | 8.83k | aLocaleItem[LocaleItem::DECIMAL_SEPARATOR] = aLocaleDataItem.decimalSeparator; |
219 | 8.83k | aLocaleItem[LocaleItem::TIME_SEPARATOR] = aLocaleDataItem.timeSeparator; |
220 | 8.83k | aLocaleItem[LocaleItem::TIME_100SEC_SEPARATOR] = aLocaleDataItem.time100SecSeparator; |
221 | 8.83k | aLocaleItem[LocaleItem::LIST_SEPARATOR] = aLocaleDataItem.listSeparator; |
222 | 8.83k | aLocaleItem[LocaleItem::SINGLE_QUOTATION_START] = aLocaleDataItem.quotationStart; |
223 | 8.83k | aLocaleItem[LocaleItem::SINGLE_QUOTATION_END] = aLocaleDataItem.quotationEnd; |
224 | 8.83k | aLocaleItem[LocaleItem::DOUBLE_QUOTATION_START] = aLocaleDataItem.doubleQuotationStart; |
225 | 8.83k | aLocaleItem[LocaleItem::DOUBLE_QUOTATION_END] = aLocaleDataItem.doubleQuotationEnd; |
226 | 8.83k | aLocaleItem[LocaleItem::MEASUREMENT_SYSTEM] = aLocaleDataItem.measurementSystem; |
227 | 8.83k | aLocaleItem[LocaleItem::TIME_AM] = aLocaleDataItem.timeAM; |
228 | 8.83k | aLocaleItem[LocaleItem::TIME_PM] = aLocaleDataItem.timePM; |
229 | 8.83k | aLocaleItem[LocaleItem::LONG_DATE_DAY_OF_WEEK_SEPARATOR] = aLocaleDataItem.LongDateDayOfWeekSeparator; |
230 | 8.83k | aLocaleItem[LocaleItem::LONG_DATE_DAY_SEPARATOR] = aLocaleDataItem.LongDateDaySeparator; |
231 | 8.83k | aLocaleItem[LocaleItem::LONG_DATE_MONTH_SEPARATOR] = aLocaleDataItem.LongDateMonthSeparator; |
232 | 8.83k | aLocaleItem[LocaleItem::LONG_DATE_YEAR_SEPARATOR] = aLocaleDataItem.LongDateYearSeparator; |
233 | 8.83k | aLocaleItem[LocaleItem::DECIMAL_SEPARATOR_ALTERNATIVE] = aLocaleDataItem.decimalSeparatorAlternative; |
234 | 8.83k | } |
235 | | |
236 | | /* FIXME-BCP47: locale data should provide a language tag instead that could be |
237 | | * passed on. */ |
238 | | css::i18n::LanguageCountryInfo LocaleDataWrapper::getLanguageCountryInfo() const |
239 | 1.66M | { |
240 | 1.66M | try |
241 | 1.66M | { |
242 | 1.66M | return xLD->getLanguageCountryInfo( getMyLocale() ); |
243 | 1.66M | } |
244 | 1.66M | catch (const Exception&) |
245 | 1.66M | { |
246 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLanguageCountryInfo" ); |
247 | 0 | } |
248 | 0 | return css::i18n::LanguageCountryInfo(); |
249 | 1.66M | } |
250 | | |
251 | | const css::i18n::LocaleDataItem2& LocaleDataWrapper::getLocaleItem() const |
252 | 0 | { |
253 | 0 | return aLocaleDataItem; |
254 | 0 | } |
255 | | |
256 | | css::uno::Sequence< css::i18n::Currency2 > LocaleDataWrapper::getAllCurrencies() const |
257 | 1.66M | { |
258 | 1.66M | try |
259 | 1.66M | { |
260 | 1.66M | return xLD->getAllCurrencies2( getMyLocale() ); |
261 | 1.66M | } |
262 | 1.66M | catch (const Exception&) |
263 | 1.66M | { |
264 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCurrencies" ); |
265 | 0 | } |
266 | 0 | return {}; |
267 | 1.66M | } |
268 | | |
269 | | css::uno::Sequence< css::i18n::FormatElement > LocaleDataWrapper::getAllFormats() const |
270 | 0 | { |
271 | 0 | try |
272 | 0 | { |
273 | 0 | return xLD->getAllFormats( getMyLocale() ); |
274 | 0 | } |
275 | 0 | catch (const Exception&) |
276 | 0 | { |
277 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllFormats" ); |
278 | 0 | } |
279 | 0 | return {}; |
280 | 0 | } |
281 | | |
282 | | css::i18n::ForbiddenCharacters LocaleDataWrapper::getForbiddenCharacters() const |
283 | 30.5k | { |
284 | 30.5k | try |
285 | 30.5k | { |
286 | 30.5k | return xLD->getForbiddenCharacters( getMyLocale() ); |
287 | 30.5k | } |
288 | 30.5k | catch (const Exception&) |
289 | 30.5k | { |
290 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "getForbiddenCharacters" ); |
291 | 0 | } |
292 | 0 | return css::i18n::ForbiddenCharacters(); |
293 | 30.5k | } |
294 | | |
295 | | const css::uno::Sequence< css::lang::Locale > & LocaleDataWrapper::getAllInstalledLocaleNames() const |
296 | 14 | { |
297 | 14 | uno::Sequence< lang::Locale > &rInstalledLocales = gInstalledLocales; |
298 | | |
299 | 14 | if ( rInstalledLocales.hasElements() ) |
300 | 0 | return rInstalledLocales; |
301 | | |
302 | 14 | try |
303 | 14 | { |
304 | 14 | rInstalledLocales = xLD->getAllInstalledLocaleNames(); |
305 | 14 | } |
306 | 14 | catch ( const Exception& ) |
307 | 14 | { |
308 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllInstalledLocaleNames" ); |
309 | 0 | } |
310 | 14 | return rInstalledLocales; |
311 | 14 | } |
312 | | |
313 | | // --- Impl and helpers ---------------------------------------------------- |
314 | | |
315 | | // static |
316 | | const css::uno::Sequence< css::lang::Locale >& LocaleDataWrapper::getInstalledLocaleNames() |
317 | 14 | { |
318 | 14 | const uno::Sequence< lang::Locale > &rInstalledLocales = gInstalledLocales; |
319 | | |
320 | 14 | if ( !rInstalledLocales.hasElements() ) |
321 | 14 | { |
322 | 14 | LocaleDataWrapper aLDW( ::comphelper::getProcessComponentContext(), LanguageTag( LANGUAGE_SYSTEM) ); |
323 | 14 | aLDW.getAllInstalledLocaleNames(); |
324 | 14 | } |
325 | 14 | return rInstalledLocales; |
326 | 14 | } |
327 | | |
328 | | // static |
329 | | const std::vector< LanguageType >& LocaleDataWrapper::getInstalledLanguageTypes() |
330 | 0 | { |
331 | 0 | std::vector< LanguageType > &rInstalledLanguageTypes = gInstalledLanguageTypes; |
332 | |
|
333 | 0 | if ( !rInstalledLanguageTypes.empty() ) |
334 | 0 | return rInstalledLanguageTypes; |
335 | | |
336 | 0 | const css::uno::Sequence< css::lang::Locale > xLoc = getInstalledLocaleNames(); |
337 | 0 | sal_Int32 nCount = xLoc.getLength(); |
338 | 0 | std::vector< LanguageType > xLang; |
339 | 0 | xLang.reserve(nCount); |
340 | 0 | for ( const auto& rLoc : xLoc ) |
341 | 0 | { |
342 | 0 | LanguageTag aLanguageTag( rLoc ); |
343 | 0 | OUString aDebugLocale; |
344 | 0 | if (areChecksEnabled()) |
345 | 0 | { |
346 | 0 | aDebugLocale = aLanguageTag.getBcp47( false); |
347 | 0 | } |
348 | |
|
349 | 0 | LanguageType eLang = aLanguageTag.getLanguageType( false); |
350 | 0 | if (areChecksEnabled() && eLang == LANGUAGE_DONTKNOW) |
351 | 0 | { |
352 | 0 | OUString aMsg = "ConvertIsoNamesToLanguage: unknown MS-LCID for locale\n" + |
353 | 0 | aDebugLocale; |
354 | 0 | outputCheckMessage(aMsg); |
355 | 0 | } |
356 | |
|
357 | 0 | if ( eLang == LANGUAGE_NORWEGIAN) // no_NO, not Bokmal (nb_NO), not Nynorsk (nn_NO) |
358 | 0 | eLang = LANGUAGE_DONTKNOW; // don't offer "Unknown" language |
359 | 0 | if ( eLang != LANGUAGE_DONTKNOW ) |
360 | 0 | { |
361 | 0 | LanguageTag aBackLanguageTag( eLang); |
362 | 0 | if ( aLanguageTag != aBackLanguageTag ) |
363 | 0 | { |
364 | | // In checks, exclude known problems because no MS-LCID defined |
365 | | // and default for Language found. |
366 | 0 | if ( areChecksEnabled() |
367 | 0 | && aDebugLocale != "ar-SD" // Sudan/ar |
368 | 0 | && aDebugLocale != "en-CB" // Caribbean is not a country |
369 | | // && aDebugLocale != "en-BG" // ?!? Bulgaria/en |
370 | | // && aDebugLocale != "es-BR" // ?!? Brazil/es |
371 | 0 | ) |
372 | 0 | { |
373 | 0 | outputCheckMessage(Concat2View( |
374 | 0 | "ConvertIsoNamesToLanguage/ConvertLanguageToIsoNames: ambiguous locale (MS-LCID?)\n" |
375 | 0 | + aDebugLocale |
376 | 0 | + " -> 0x" |
377 | 0 | + OUString::number(static_cast<sal_Int32>(static_cast<sal_uInt16>(eLang)), 16) |
378 | 0 | + " -> " |
379 | 0 | + aBackLanguageTag.getBcp47() )); |
380 | 0 | } |
381 | 0 | eLang = LANGUAGE_DONTKNOW; |
382 | 0 | } |
383 | 0 | } |
384 | 0 | if ( eLang != LANGUAGE_DONTKNOW ) |
385 | 0 | xLang.push_back(eLang); |
386 | 0 | } |
387 | 0 | rInstalledLanguageTypes = std::move(xLang); |
388 | |
|
389 | 0 | return rInstalledLanguageTypes; |
390 | 0 | } |
391 | | |
392 | | const OUString& LocaleDataWrapper::getOneLocaleItem( sal_Int16 nItem ) const |
393 | 341M | { |
394 | 341M | if ( nItem >= LocaleItem::COUNT2 ) |
395 | 0 | { |
396 | 0 | SAL_WARN( "unotools.i18n", "getOneLocaleItem: bounds" ); |
397 | 0 | return aLocaleItem[0]; |
398 | 0 | } |
399 | 341M | return aLocaleItem[nItem]; |
400 | 341M | } |
401 | | |
402 | | const OUString& LocaleDataWrapper::getOneReservedWord( sal_Int16 nWord ) const |
403 | 3.34M | { |
404 | 3.34M | if ( nWord < 0 || o3tl::make_unsigned(nWord) >= aReservedWords.size() ) |
405 | 0 | { |
406 | 0 | SAL_WARN( "unotools.i18n", "getOneReservedWord: bounds" ); |
407 | 0 | return EMPTY_OUSTRING; |
408 | 0 | } |
409 | 3.34M | return aReservedWords[nWord]; |
410 | 3.34M | } |
411 | | |
412 | | // static |
413 | | MeasurementSystem LocaleDataWrapper::mapMeasurementStringToEnum( std::u16string_view rMS ) |
414 | 151k | { |
415 | | //! TODO: could be cached too |
416 | 151k | if ( o3tl::equalsIgnoreAsciiCase( rMS, u"metric" ) ) |
417 | 0 | return MeasurementSystem::Metric; |
418 | | //! TODO: other measurement systems? => extend enum MeasurementSystem |
419 | 151k | return MeasurementSystem::US; |
420 | 151k | } |
421 | | |
422 | | bool LocaleDataWrapper::doesSecondaryCalendarUseEC( std::u16string_view rName ) const |
423 | 0 | { |
424 | 0 | if (rName.empty()) |
425 | 0 | return false; |
426 | | |
427 | | // Check language tag first to avoid loading all calendars of this locale. |
428 | 0 | LanguageTag aLoaded( getLoadedLanguageTag()); |
429 | 0 | const OUString& aBcp47( aLoaded.getBcp47()); |
430 | | // So far determine only by locale, we know for a few. |
431 | | /* TODO: check date format codes? or add to locale data? */ |
432 | 0 | if ( aBcp47 != "ja-JP" && |
433 | 0 | aBcp47 != "lo-LA" && |
434 | 0 | aBcp47 != "zh-TW") |
435 | 0 | return false; |
436 | | |
437 | 0 | if (!xSecondaryCalendar) |
438 | 0 | return false; |
439 | 0 | if (!xSecondaryCalendar->Name.equalsIgnoreAsciiCase( rName)) |
440 | 0 | return false; |
441 | | |
442 | 0 | return true; |
443 | 0 | } |
444 | | |
445 | | const std::shared_ptr< css::i18n::Calendar2 >& LocaleDataWrapper::getDefaultCalendar() const |
446 | 0 | { |
447 | 0 | return xDefaultCalendar; |
448 | 0 | } |
449 | | |
450 | | css::uno::Sequence< css::i18n::CalendarItem2 > const & LocaleDataWrapper::getDefaultCalendarDays() const |
451 | 0 | { |
452 | 0 | return getDefaultCalendar()->Days; |
453 | 0 | } |
454 | | |
455 | | css::uno::Sequence< css::i18n::CalendarItem2 > const & LocaleDataWrapper::getDefaultCalendarMonths() const |
456 | 0 | { |
457 | 0 | return getDefaultCalendar()->Months; |
458 | 0 | } |
459 | | |
460 | | // --- currencies ----------------------------------------------------- |
461 | | |
462 | | const OUString& LocaleDataWrapper::getCurrSymbol() const |
463 | 601 | { |
464 | 601 | return aCurrSymbol; |
465 | 601 | } |
466 | | |
467 | | const OUString& LocaleDataWrapper::getCurrBankSymbol() const |
468 | 1.11M | { |
469 | 1.11M | return aCurrBankSymbol; |
470 | 1.11M | } |
471 | | |
472 | | sal_uInt16 LocaleDataWrapper::getCurrPositiveFormat() const |
473 | 216k | { |
474 | 216k | return nCurrPositiveFormat; |
475 | 216k | } |
476 | | |
477 | | sal_uInt16 LocaleDataWrapper::getCurrNegativeFormat() const |
478 | 216k | { |
479 | 216k | return nCurrNegativeFormat; |
480 | 216k | } |
481 | | |
482 | | sal_uInt16 LocaleDataWrapper::getCurrDigits() const |
483 | 597 | { |
484 | 597 | return nCurrDigits; |
485 | 597 | } |
486 | | |
487 | | void LocaleDataWrapper::scanCurrFormatImpl( std::u16string_view rCode, |
488 | | sal_Int32 nStart, sal_Int32& nSign, sal_Int32& nPar, |
489 | | sal_Int32& nNum, sal_Int32& nBlank, sal_Int32& nSym ) const |
490 | 17.6k | { |
491 | 17.6k | nSign = nPar = nNum = nBlank = nSym = -1; |
492 | 17.6k | const sal_Unicode* const pStr = rCode.data(); |
493 | 17.6k | const sal_Unicode* const pStop = pStr + rCode.size(); |
494 | 17.6k | const sal_Unicode* p = pStr + nStart; |
495 | 17.6k | int nInSection = 0; |
496 | 17.6k | bool bQuote = false; |
497 | 363k | while ( p < pStop ) |
498 | 345k | { |
499 | 345k | if ( bQuote ) |
500 | 0 | { |
501 | 0 | if ( *p == '"' && *(p-1) != '\\' ) |
502 | 0 | bQuote = false; |
503 | 0 | } |
504 | 345k | else |
505 | 345k | { |
506 | 345k | switch ( *p ) |
507 | 345k | { |
508 | 0 | case '"' : |
509 | 0 | if ( pStr == p || *(p-1) != '\\' ) |
510 | 0 | bQuote = true; |
511 | 0 | break; |
512 | 26.4k | case '-' : |
513 | 26.4k | if (!nInSection && nSign == -1) |
514 | 8.83k | nSign = p - pStr; |
515 | 26.4k | break; |
516 | 0 | case '(' : |
517 | 0 | if (!nInSection && nPar == -1) |
518 | 0 | nPar = p - pStr; |
519 | 0 | break; |
520 | 70.9k | case '0' : |
521 | 123k | case '#' : |
522 | 123k | if (!nInSection && nNum == -1) |
523 | 17.6k | nNum = p - pStr; |
524 | 123k | break; |
525 | 26.4k | case '[' : |
526 | 26.4k | nInSection++; |
527 | 26.4k | break; |
528 | 26.4k | case ']' : |
529 | 26.4k | if ( nInSection ) |
530 | 26.4k | { |
531 | 26.4k | nInSection--; |
532 | 26.4k | if (!nInSection && nBlank == -1 |
533 | 26.4k | && nSym != -1 && p < pStop-1 && *(p+1) == ' ' ) |
534 | 56 | nBlank = p - pStr + 1; |
535 | 26.4k | } |
536 | 26.4k | break; |
537 | 34.6k | case '$' : |
538 | 34.6k | if (nSym == -1 && nInSection && *(p-1) == '[') |
539 | 17.6k | { |
540 | 17.6k | nSym = p - pStr + 1; |
541 | 17.6k | if (nNum != -1 && *(p-2) == ' ') |
542 | 0 | nBlank = p - pStr - 2; |
543 | 17.6k | } |
544 | 34.6k | break; |
545 | 8.83k | case ';' : |
546 | 8.83k | if ( !nInSection ) |
547 | 8.83k | p = pStop; |
548 | 8.83k | break; |
549 | 98.9k | default: |
550 | 98.9k | if (!nInSection && nSym == -1 && o3tl::starts_with(rCode.substr(static_cast<sal_Int32>(p - pStr)), aCurrSymbol)) |
551 | 0 | { // currency symbol not surrounded by [$...] |
552 | 0 | nSym = p - pStr; |
553 | 0 | if (nBlank == -1 && pStr < p && *(p-1) == ' ') |
554 | 0 | nBlank = p - pStr - 1; |
555 | 0 | p += aCurrSymbol.getLength() - 1; |
556 | 0 | if (nBlank == -1 && p < pStop-2 && *(p+2) == ' ') |
557 | 0 | nBlank = p - pStr + 2; |
558 | 0 | } |
559 | 345k | } |
560 | 345k | } |
561 | 345k | p++; |
562 | 345k | } |
563 | 17.6k | } |
564 | | |
565 | | void LocaleDataWrapper::loadCurrencyFormats() |
566 | 9.92k | { |
567 | 9.92k | css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext ); |
568 | 9.92k | uno::Sequence< NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( KNumberFormatUsage::CURRENCY, maLanguageTag.getLocale() ); |
569 | 9.92k | sal_Int32 nCnt = aFormatSeq.getLength(); |
570 | 9.92k | if ( !nCnt ) |
571 | 0 | { // bad luck |
572 | 0 | if (areChecksEnabled()) |
573 | 0 | { |
574 | 0 | outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrFormatsImpl: no currency formats" ) ); |
575 | 0 | } |
576 | 0 | nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault; |
577 | 0 | return; |
578 | 0 | } |
579 | | // find a negative code (medium preferred) and a default (medium preferred) (not necessarily the same) |
580 | 9.92k | NumberFormatCode const * const pFormatArr = aFormatSeq.getArray(); |
581 | 9.92k | sal_Int32 nElem, nDef, nNeg, nMedium; |
582 | 9.92k | nDef = nNeg = nMedium = -1; |
583 | 115k | for ( nElem = 0; nElem < nCnt; nElem++ ) |
584 | 105k | { |
585 | 105k | if ( pFormatArr[nElem].Type == KNumberFormatType::MEDIUM ) |
586 | 88.1k | { |
587 | 88.1k | if ( pFormatArr[nElem].Default ) |
588 | 8.83k | { |
589 | 8.83k | nDef = nElem; |
590 | 8.83k | nMedium = nElem; |
591 | 8.83k | if ( pFormatArr[nElem].Code.indexOf( ';' ) >= 0 ) |
592 | 8.83k | nNeg = nElem; |
593 | 8.83k | } |
594 | 79.3k | else |
595 | 79.3k | { |
596 | 79.3k | if ( (nNeg == -1 || nMedium == -1) && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 ) |
597 | 8.53k | nNeg = nElem; |
598 | 79.3k | if ( nMedium == -1 ) |
599 | 8.83k | nMedium = nElem; |
600 | 79.3k | } |
601 | 88.1k | } |
602 | 17.5k | else |
603 | 17.5k | { |
604 | 17.5k | if ( nDef == -1 && pFormatArr[nElem].Default ) |
605 | 8.83k | nDef = nElem; |
606 | 17.5k | if ( nNeg == -1 && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 ) |
607 | 8.83k | nNeg = nElem; |
608 | 17.5k | } |
609 | 105k | } |
610 | | |
611 | 9.92k | sal_Int32 nSign, nPar, nNum, nBlank, nSym; |
612 | | |
613 | | // positive format |
614 | 9.92k | nElem = (nDef >= 0 ? nDef : (nNeg >= 0 ? nNeg : 0)); |
615 | 9.92k | scanCurrFormatImpl( pFormatArr[nElem].Code, 0, nSign, nPar, nNum, nBlank, nSym ); |
616 | 9.92k | if (areChecksEnabled() && (nNum == -1 || nSym == -1)) |
617 | 0 | { |
618 | 0 | outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrFormatsImpl: CurrPositiveFormat?" ) ); |
619 | 0 | } |
620 | 9.92k | if (nBlank == -1) |
621 | 8.78k | { |
622 | 8.78k | if ( nSym < nNum ) |
623 | 8.78k | nCurrPositiveFormat = 0; // $1 |
624 | 0 | else |
625 | 0 | nCurrPositiveFormat = 1; // 1$ |
626 | 8.78k | } |
627 | 1.13k | else |
628 | 1.13k | { |
629 | 1.13k | if ( nSym < nNum ) |
630 | 42 | nCurrPositiveFormat = 2; // $ 1 |
631 | 1.08k | else |
632 | 1.08k | nCurrPositiveFormat = 3; // 1 $ |
633 | 1.13k | } |
634 | | |
635 | | // negative format |
636 | 9.92k | if ( nNeg < 0 ) |
637 | 0 | nCurrNegativeFormat = nCurrFormatDefault; |
638 | 9.92k | else |
639 | 9.92k | { |
640 | 9.92k | const OUString& rCode = pFormatArr[nNeg].Code; |
641 | 9.92k | sal_Int32 nDelim = rCode.indexOf(';'); |
642 | 9.92k | scanCurrFormatImpl( rCode, nDelim+1, nSign, nPar, nNum, nBlank, nSym ); |
643 | 9.92k | if (areChecksEnabled() && (nNum == -1 || nSym == -1 || (nPar == -1 && nSign == -1))) |
644 | 0 | { |
645 | 0 | outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrFormatsImpl: CurrNegativeFormat?" ) ); |
646 | 0 | } |
647 | | // NOTE: one of nPar or nSign are allowed to be -1 |
648 | 9.92k | if (nBlank == -1) |
649 | 8.81k | { |
650 | 8.81k | if ( nSym < nNum ) |
651 | 8.81k | { |
652 | 8.81k | if ( -1 < nPar && nPar < nSym ) |
653 | 0 | nCurrNegativeFormat = 0; // ($1) |
654 | 8.81k | else if ( -1 < nSign && nSign < nSym ) |
655 | 8.78k | nCurrNegativeFormat = 1; // -$1 |
656 | 28 | else if ( nNum < nSign ) |
657 | 0 | nCurrNegativeFormat = 3; // $1- |
658 | 28 | else |
659 | 28 | nCurrNegativeFormat = 2; // $-1 |
660 | 8.81k | } |
661 | 0 | else |
662 | 0 | { |
663 | 0 | if ( -1 < nPar && nPar < nNum ) |
664 | 0 | nCurrNegativeFormat = 4; // (1$) |
665 | 0 | else if ( -1 < nSign && nSign < nNum ) |
666 | 0 | nCurrNegativeFormat = 5; // -1$ |
667 | 0 | else if ( nSym < nSign ) |
668 | 0 | nCurrNegativeFormat = 7; // 1$- |
669 | 0 | else |
670 | 0 | nCurrNegativeFormat = 6; // 1-$ |
671 | 0 | } |
672 | 8.81k | } |
673 | 1.10k | else |
674 | 1.10k | { |
675 | 1.10k | if ( nSym < nNum ) |
676 | 14 | { |
677 | 14 | if ( -1 < nPar && nPar < nSym ) |
678 | 0 | nCurrNegativeFormat = 14; // ($ 1) |
679 | 14 | else if ( -1 < nSign && nSign < nSym ) |
680 | 0 | nCurrNegativeFormat = 9; // -$ 1 |
681 | 14 | else if ( nNum < nSign ) |
682 | 0 | nCurrNegativeFormat = 12; // $ 1- |
683 | 14 | else |
684 | 14 | nCurrNegativeFormat = 11; // $ -1 |
685 | 14 | } |
686 | 1.08k | else |
687 | 1.08k | { |
688 | 1.08k | if ( -1 < nPar && nPar < nNum ) |
689 | 0 | nCurrNegativeFormat = 15; // (1 $) |
690 | 1.08k | else if ( -1 < nSign && nSign < nNum ) |
691 | 0 | nCurrNegativeFormat = 8; // -1 $ |
692 | 1.08k | else if ( nSym < nSign ) |
693 | 0 | nCurrNegativeFormat = 10; // 1 $- |
694 | 1.08k | else |
695 | 1.08k | nCurrNegativeFormat = 13; // 1- $ |
696 | 1.08k | } |
697 | 1.10k | } |
698 | 9.92k | } |
699 | 9.92k | } |
700 | | |
701 | | // --- date ----------------------------------------------------------- |
702 | | |
703 | | DateOrder LocaleDataWrapper::getDateOrder() const |
704 | 31.1M | { |
705 | 31.1M | return nDateOrder; |
706 | 31.1M | } |
707 | | |
708 | | LongDateOrder LocaleDataWrapper::getLongDateOrder() const |
709 | 19.9k | { |
710 | 19.9k | return nLongDateOrder; |
711 | 19.9k | } |
712 | | |
713 | | LongDateOrder LocaleDataWrapper::scanDateOrderImpl( std::u16string_view rCode ) const |
714 | 17.6k | { |
715 | | // Only some european versions were translated, the ones with different |
716 | | // keyword combinations are: |
717 | | // English DMY, German TMJ, Spanish DMA, French JMA, Italian GMA, |
718 | | // Dutch DMJ, Finnish PKV |
719 | | |
720 | | // default is English keywords for every other language |
721 | 17.6k | size_t nDay = rCode.find('D'); |
722 | 17.6k | size_t nMonth = rCode.find('M'); |
723 | 17.6k | size_t nYear = rCode.find('Y'); |
724 | 17.6k | if (nDay == std::u16string_view::npos || nMonth == std::u16string_view::npos || nYear == std::u16string_view::npos) |
725 | 0 | { // This algorithm assumes that all three parts (DMY) are present |
726 | 0 | if (nMonth == std::u16string_view::npos) |
727 | 0 | { // only Finnish has something else than 'M' for month |
728 | 0 | nMonth = rCode.find('K'); |
729 | 0 | if (nMonth != std::u16string_view::npos) |
730 | 0 | { |
731 | 0 | nDay = rCode.find('P'); |
732 | 0 | nYear = rCode.find('V'); |
733 | 0 | } |
734 | 0 | } |
735 | 0 | else if (nDay == std::u16string_view::npos) |
736 | 0 | { // We have a month 'M' if we reach this branch. |
737 | | // Possible languages containing 'M' but no 'D': |
738 | | // German, French, Italian |
739 | 0 | nDay = rCode.find('T'); // German |
740 | 0 | if (nDay != std::u16string_view::npos) |
741 | 0 | nYear = rCode.find('J'); |
742 | 0 | else |
743 | 0 | { |
744 | 0 | nYear = rCode.find('A'); // French, Italian |
745 | 0 | if (nYear != std::u16string_view::npos) |
746 | 0 | { |
747 | 0 | nDay = rCode.find('J'); // French |
748 | 0 | if (nDay == std::u16string_view::npos) |
749 | 0 | nDay = rCode.find('G'); // Italian |
750 | 0 | } |
751 | 0 | } |
752 | 0 | } |
753 | 0 | else |
754 | 0 | { // We have a month 'M' and a day 'D'. |
755 | | // Possible languages containing 'D' and 'M' but not 'Y': |
756 | | // Spanish, Dutch |
757 | 0 | nYear = rCode.find('A'); // Spanish |
758 | 0 | if (nYear == std::u16string_view::npos) |
759 | 0 | nYear = rCode.find('J'); // Dutch |
760 | 0 | } |
761 | 0 | if (nDay == std::u16string_view::npos || nMonth == std::u16string_view::npos || nYear == std::u16string_view::npos) |
762 | 0 | { |
763 | 0 | if (areChecksEnabled()) |
764 | 0 | { |
765 | 0 | outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::scanDateOrder: not all DMY present" ) ); |
766 | 0 | } |
767 | 0 | if (nDay == std::u16string_view::npos) |
768 | 0 | nDay = rCode.size(); |
769 | 0 | if (nMonth == std::u16string_view::npos) |
770 | 0 | nMonth = rCode.size(); |
771 | 0 | if (nYear == std::u16string_view::npos) |
772 | 0 | nYear = rCode.size(); |
773 | 0 | } |
774 | 0 | } |
775 | | // compare with <= because each position may equal rCode.getLength() |
776 | 17.6k | if ( nDay <= nMonth && nMonth <= nYear ) |
777 | 748 | return LongDateOrder::DMY; // also if every position equals rCode.getLength() |
778 | 16.9k | else if ( nMonth <= nDay && nDay <= nYear ) |
779 | 16.8k | return LongDateOrder::MDY; |
780 | 80 | else if ( nYear <= nMonth && nMonth <= nDay ) |
781 | 80 | return LongDateOrder::YMD; |
782 | 0 | else if ( nYear <= nDay && nDay <= nMonth ) |
783 | 0 | return LongDateOrder::YDM; |
784 | 0 | else |
785 | 0 | { |
786 | 0 | if (areChecksEnabled()) |
787 | 0 | { |
788 | 0 | outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::scanDateOrder: no magic applicable" ) ); |
789 | 0 | } |
790 | 0 | return LongDateOrder::DMY; |
791 | 0 | } |
792 | 17.6k | } |
793 | | |
794 | | static DateOrder getDateOrderFromLongDateOrder( LongDateOrder eLong ) |
795 | 8.83k | { |
796 | 8.83k | switch (eLong) |
797 | 8.83k | { |
798 | 80 | case LongDateOrder::YMD: |
799 | 80 | return DateOrder::YMD; |
800 | 0 | break; |
801 | 353 | case LongDateOrder::DMY: |
802 | 353 | return DateOrder::DMY; |
803 | 0 | break; |
804 | 8.39k | case LongDateOrder::MDY: |
805 | 8.39k | return DateOrder::MDY; |
806 | 0 | break; |
807 | 0 | case LongDateOrder::YDM: |
808 | 0 | default: |
809 | 0 | assert(!"unhandled LongDateOrder to DateOrder"); |
810 | 0 | return DateOrder::DMY; |
811 | 8.83k | } |
812 | 8.83k | } |
813 | | |
814 | | void LocaleDataWrapper::loadDateOrders() |
815 | 8.83k | { |
816 | 8.83k | css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext ); |
817 | 8.83k | uno::Sequence< NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( KNumberFormatUsage::DATE, maLanguageTag.getLocale() ); |
818 | 8.83k | sal_Int32 nCnt = aFormatSeq.getLength(); |
819 | 8.83k | if ( !nCnt ) |
820 | 0 | { // bad luck |
821 | 0 | if (areChecksEnabled()) |
822 | 0 | { |
823 | 0 | outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no date formats" ) ); |
824 | 0 | } |
825 | 0 | nDateOrder = DateOrder::DMY; |
826 | 0 | nLongDateOrder = LongDateOrder::DMY; |
827 | 0 | return; |
828 | 0 | } |
829 | | // find the edit (21), a default (medium preferred), |
830 | | // a medium (default preferred), and a long (default preferred) |
831 | 8.83k | NumberFormatCode const * const pFormatArr = aFormatSeq.getArray(); |
832 | 8.83k | sal_Int32 nEdit, nDef, nMedium, nLong; |
833 | 8.83k | nEdit = nDef = nMedium = nLong = -1; |
834 | 282k | for ( sal_Int32 nElem = 0; nElem < nCnt; nElem++ ) |
835 | 273k | { |
836 | 273k | if ( nEdit == -1 && pFormatArr[nElem].Index == NumberFormatIndex::DATE_SYS_DDMMYYYY ) |
837 | 8.83k | nEdit = nElem; |
838 | 273k | if ( nDef == -1 && pFormatArr[nElem].Default ) |
839 | 8.83k | nDef = nElem; |
840 | 273k | switch ( pFormatArr[nElem].Type ) |
841 | 273k | { |
842 | 132k | case KNumberFormatType::MEDIUM : |
843 | 132k | { |
844 | 132k | if ( pFormatArr[nElem].Default ) |
845 | 8.83k | { |
846 | 8.83k | nDef = nElem; |
847 | 8.83k | nMedium = nElem; |
848 | 8.83k | } |
849 | 123k | else if ( nMedium == -1 ) |
850 | 348 | nMedium = nElem; |
851 | 132k | } |
852 | 132k | break; |
853 | 123k | case KNumberFormatType::LONG : |
854 | 123k | { |
855 | 123k | if ( pFormatArr[nElem].Default ) |
856 | 8.83k | nLong = nElem; |
857 | 114k | else if ( nLong == -1 ) |
858 | 0 | nLong = nElem; |
859 | 123k | } |
860 | 123k | break; |
861 | 273k | } |
862 | 273k | } |
863 | 8.83k | if ( nEdit == -1 ) |
864 | 0 | { |
865 | 0 | if (areChecksEnabled()) |
866 | 0 | { |
867 | 0 | outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no edit" ) ); |
868 | 0 | } |
869 | 0 | if ( nDef == -1 ) |
870 | 0 | { |
871 | 0 | if (areChecksEnabled()) |
872 | 0 | { |
873 | 0 | outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no default" ) ); |
874 | 0 | } |
875 | 0 | if ( nMedium != -1 ) |
876 | 0 | nDef = nMedium; |
877 | 0 | else if ( nLong != -1 ) |
878 | 0 | nDef = nLong; |
879 | 0 | else |
880 | 0 | nDef = 0; |
881 | 0 | } |
882 | 0 | nEdit = nDef; |
883 | 0 | } |
884 | 8.83k | LongDateOrder nDO = scanDateOrderImpl( pFormatArr[nEdit].Code ); |
885 | 8.83k | if ( pFormatArr[nEdit].Type == KNumberFormatType::LONG ) |
886 | 0 | { // normally this is not the case |
887 | 0 | nLongDateOrder = nDO; |
888 | 0 | nDateOrder = getDateOrderFromLongDateOrder(nDO); |
889 | 0 | } |
890 | 8.83k | else |
891 | 8.83k | { |
892 | | // YDM should not occur in a short/medium date (i.e. no locale has |
893 | | // that) and is nowhere handled. |
894 | 8.83k | nDateOrder = getDateOrderFromLongDateOrder(nDO); |
895 | 8.83k | if ( nLong == -1 ) |
896 | 0 | nLongDateOrder = nDO; |
897 | 8.83k | else |
898 | 8.83k | nLongDateOrder = scanDateOrderImpl( pFormatArr[nLong].Code ); |
899 | 8.83k | } |
900 | 8.83k | } |
901 | | |
902 | | // --- digit grouping ------------------------------------------------- |
903 | | |
904 | | void LocaleDataWrapper::loadDigitGrouping() |
905 | 8.83k | { |
906 | | /* TODO: This is a very simplified grouping setup that only serves its |
907 | | * current purpose for Indian locales. A free-form flexible one would |
908 | | * obtain grouping from locale data where it could be specified using, for |
909 | | * example, codes like #,### and #,##,### that would generate the integer |
910 | | * sequence. Needed additional API and a locale data element. |
911 | | */ |
912 | | |
913 | 8.83k | if (aGrouping.hasElements() && aGrouping[0]) |
914 | 0 | return; |
915 | | |
916 | 8.83k | i18n::LanguageCountryInfo aLCInfo( getLanguageCountryInfo()); |
917 | 8.83k | if (aLCInfo.Country.equalsIgnoreAsciiCase("IN") || // India |
918 | 8.79k | aLCInfo.Country.equalsIgnoreAsciiCase("BT") ) // Bhutan |
919 | 34 | { |
920 | 34 | aGrouping = { 3, 2, 0 }; |
921 | 34 | } |
922 | 8.79k | else |
923 | 8.79k | { |
924 | 8.79k | aGrouping = { 3, 0, 0 }; |
925 | 8.79k | } |
926 | 8.83k | } |
927 | | |
928 | | const css::uno::Sequence< sal_Int32 >& LocaleDataWrapper::getDigitGrouping() const |
929 | 9.41M | { |
930 | 9.41M | return aGrouping; |
931 | 9.41M | } |
932 | | |
933 | | // --- simple number formatting helpers ------------------------------- |
934 | | |
935 | | // The ImplAdd... methods are taken from class International and modified to |
936 | | // suit the needs. |
937 | | |
938 | | static void ImplAddUNum( OUStringBuffer& rBuf, sal_uInt64 nNumber ) |
939 | 0 | { |
940 | | // fill temp buffer with digits |
941 | 0 | sal_Unicode aTempBuf[64]; |
942 | 0 | sal_Unicode* pTempBuf = aTempBuf; |
943 | 0 | do |
944 | 0 | { |
945 | 0 | *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0'; |
946 | 0 | pTempBuf++; |
947 | 0 | nNumber /= 10; |
948 | 0 | } |
949 | 0 | while ( nNumber ); |
950 | | |
951 | | // copy temp buffer to buffer passed |
952 | 0 | do |
953 | 0 | { |
954 | 0 | pTempBuf--; |
955 | 0 | rBuf.append(*pTempBuf); |
956 | 0 | } |
957 | 0 | while ( pTempBuf != aTempBuf ); |
958 | 0 | } |
959 | | |
960 | | static void ImplAddUNum( OUStringBuffer& rBuf, sal_uInt64 nNumber, int nMinLen ) |
961 | 717k | { |
962 | | // fill temp buffer with digits |
963 | 717k | sal_Unicode aTempBuf[64]; |
964 | 717k | sal_Unicode* pTempBuf = aTempBuf; |
965 | 717k | do |
966 | 871k | { |
967 | 871k | *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0'; |
968 | 871k | pTempBuf++; |
969 | 871k | nNumber /= 10; |
970 | 871k | nMinLen--; |
971 | 871k | } |
972 | 871k | while ( nNumber ); |
973 | | |
974 | | // fill with zeros up to the minimal length |
975 | 2.71M | while ( nMinLen > 0 ) |
976 | 1.99M | { |
977 | 1.99M | rBuf.append('0'); |
978 | 1.99M | nMinLen--; |
979 | 1.99M | } |
980 | | |
981 | | // copy temp buffer to real buffer |
982 | 717k | do |
983 | 871k | { |
984 | 871k | pTempBuf--; |
985 | 871k | rBuf.append(*pTempBuf); |
986 | 871k | } |
987 | 871k | while ( pTempBuf != aTempBuf ); |
988 | 717k | } |
989 | | |
990 | | static void ImplAddNum( OUStringBuffer& rBuf, sal_Int64 nNumber, int nMinLen ) |
991 | 717k | { |
992 | 717k | if (nNumber < 0) |
993 | 0 | { |
994 | 0 | rBuf.append('-'); |
995 | 0 | nNumber = -nNumber; |
996 | 0 | } |
997 | 717k | return ImplAddUNum( rBuf, nNumber, nMinLen); |
998 | 717k | } |
999 | | |
1000 | | static void ImplAdd2UNum( OUStringBuffer& rBuf, sal_uInt16 nNumber ) |
1001 | 5.04M | { |
1002 | 5.04M | DBG_ASSERT( nNumber < 100, "ImplAdd2UNum() - Number >= 100" ); |
1003 | | |
1004 | 5.04M | if ( nNumber < 10 ) |
1005 | 4.83M | { |
1006 | 4.83M | rBuf.append('0'); |
1007 | 4.83M | rBuf.append(static_cast<char>(nNumber + '0')); |
1008 | 4.83M | } |
1009 | 202k | else |
1010 | 202k | { |
1011 | 202k | sal_uInt16 nTemp = nNumber % 10; |
1012 | 202k | nNumber /= 10; |
1013 | 202k | rBuf.append(static_cast<char>(nNumber + '0')); |
1014 | 202k | rBuf.append(static_cast<char>(nTemp + '0')); |
1015 | 202k | } |
1016 | 5.04M | } |
1017 | | |
1018 | | static void ImplAdd9UNum( OUStringBuffer& rBuf, sal_uInt32 nNumber ) |
1019 | 0 | { |
1020 | 0 | DBG_ASSERT( nNumber < 1000000000, "ImplAdd9UNum() - Number >= 1000000000" ); |
1021 | |
|
1022 | 0 | std::ostringstream ostr; |
1023 | 0 | ostr.fill('0'); |
1024 | 0 | ostr.width(9); |
1025 | 0 | ostr << nNumber; |
1026 | 0 | std::string aStr = ostr.str(); |
1027 | 0 | rBuf.appendAscii(aStr.c_str(), aStr.size()); |
1028 | 0 | } |
1029 | | |
1030 | | void LocaleDataWrapper::ImplAddFormatNum( OUStringBuffer& rBuf, |
1031 | | sal_Int64 nNumber, sal_uInt16 nDecimals, bool bUseThousandSep, |
1032 | | bool bTrailingZeros ) const |
1033 | 0 | { |
1034 | 0 | OUStringBuffer aNumBuf(64); |
1035 | 0 | sal_uInt16 nNumLen; |
1036 | | |
1037 | | // negative number |
1038 | 0 | sal_uInt64 abs; |
1039 | 0 | if ( nNumber < 0 ) |
1040 | 0 | { |
1041 | | // Avoid overflow, map -2^63 -> 2^63 explicitly: |
1042 | 0 | abs = nNumber == std::numeric_limits<sal_Int64>::min() |
1043 | 0 | ? static_cast<sal_uInt64>(std::numeric_limits<sal_Int64>::min()) : nNumber * -1; |
1044 | 0 | rBuf.append('-'); |
1045 | 0 | } |
1046 | 0 | else |
1047 | 0 | { |
1048 | 0 | abs = nNumber; |
1049 | 0 | } |
1050 | | |
1051 | | // convert number |
1052 | 0 | ImplAddUNum( aNumBuf, abs ); |
1053 | 0 | nNumLen = static_cast<sal_uInt16>(aNumBuf.getLength()); |
1054 | |
|
1055 | 0 | if ( nNumLen <= nDecimals ) |
1056 | 0 | { |
1057 | | // strip .0 in decimals? |
1058 | 0 | if ( !nNumber && !bTrailingZeros ) |
1059 | 0 | { |
1060 | 0 | rBuf.append('0'); |
1061 | 0 | } |
1062 | 0 | else |
1063 | 0 | { |
1064 | | // LeadingZero, insert 0 |
1065 | 0 | if ( isNumLeadingZero() ) |
1066 | 0 | { |
1067 | 0 | rBuf.append('0'); |
1068 | 0 | } |
1069 | | |
1070 | | // append decimal separator |
1071 | 0 | rBuf.append( aLocaleDataItem.decimalSeparator ); |
1072 | | |
1073 | | // fill with zeros |
1074 | 0 | sal_uInt16 i = 0; |
1075 | 0 | while ( i < (nDecimals-nNumLen) ) |
1076 | 0 | { |
1077 | 0 | rBuf.append('0'); |
1078 | 0 | i++; |
1079 | 0 | } |
1080 | | |
1081 | | // append decimals |
1082 | 0 | rBuf.append(aNumBuf); |
1083 | 0 | } |
1084 | 0 | } |
1085 | 0 | else |
1086 | 0 | { |
1087 | 0 | const OUString& rThoSep = aLocaleDataItem.thousandSeparator; |
1088 | | |
1089 | | // copy number to buffer (excluding decimals) |
1090 | 0 | sal_uInt16 nNumLen2 = nNumLen-nDecimals; |
1091 | 0 | uno::Sequence< sal_Bool > aGroupPos; |
1092 | 0 | if (bUseThousandSep) |
1093 | 0 | aGroupPos = utl::DigitGroupingIterator::createForwardSequence( |
1094 | 0 | nNumLen2, getDigitGrouping()); |
1095 | 0 | sal_uInt16 i = 0; |
1096 | 0 | for (; i < nNumLen2; ++i ) |
1097 | 0 | { |
1098 | 0 | rBuf.append(aNumBuf[i]); |
1099 | | |
1100 | | // add thousand separator? |
1101 | 0 | if ( bUseThousandSep && aGroupPos[i] ) |
1102 | 0 | rBuf.append( rThoSep ); |
1103 | 0 | } |
1104 | | |
1105 | | // append decimals |
1106 | 0 | if ( nDecimals ) |
1107 | 0 | { |
1108 | 0 | rBuf.append( aLocaleDataItem.decimalSeparator ); |
1109 | |
|
1110 | 0 | bool bNullEnd = true; |
1111 | 0 | while ( i < nNumLen ) |
1112 | 0 | { |
1113 | 0 | if ( aNumBuf[i] != '0' ) |
1114 | 0 | bNullEnd = false; |
1115 | |
|
1116 | 0 | rBuf.append(aNumBuf[i]); |
1117 | 0 | i++; |
1118 | 0 | } |
1119 | | |
1120 | | // strip .0 in decimals? |
1121 | 0 | if ( bNullEnd && !bTrailingZeros ) |
1122 | 0 | rBuf.setLength( rBuf.getLength() - (nDecimals + 1) ); |
1123 | 0 | } |
1124 | 0 | } |
1125 | 0 | } |
1126 | | |
1127 | | // --- simple date and time formatting -------------------------------- |
1128 | | |
1129 | | OUString LocaleDataWrapper::getDate( const Date& rDate ) const |
1130 | 717k | { |
1131 | | //!TODO: leading zeros et al |
1132 | 717k | OUStringBuffer aBuf(128); |
1133 | 717k | sal_uInt16 nDay = rDate.GetDay(); |
1134 | 717k | sal_uInt16 nMonth = rDate.GetMonth(); |
1135 | 717k | sal_Int16 nYear = rDate.GetYear(); |
1136 | 717k | sal_uInt16 nYearLen; |
1137 | | |
1138 | 717k | if ( (true) /* IsDateCentury() */ ) |
1139 | 717k | nYearLen = 4; |
1140 | 0 | else |
1141 | 0 | { |
1142 | 0 | nYearLen = 2; |
1143 | 0 | nYear %= 100; |
1144 | 0 | } |
1145 | | |
1146 | 717k | switch ( getDateOrder() ) |
1147 | 717k | { |
1148 | 0 | case DateOrder::DMY : |
1149 | 0 | ImplAdd2UNum( aBuf, nDay ); |
1150 | 0 | aBuf.append( aLocaleDataItem.dateSeparator ); |
1151 | 0 | ImplAdd2UNum( aBuf, nMonth ); |
1152 | 0 | aBuf.append( aLocaleDataItem.dateSeparator ); |
1153 | 0 | ImplAddNum( aBuf, nYear, nYearLen ); |
1154 | 0 | break; |
1155 | 717k | case DateOrder::MDY : |
1156 | 717k | ImplAdd2UNum( aBuf, nMonth ); |
1157 | 717k | aBuf.append( aLocaleDataItem.dateSeparator ); |
1158 | 717k | ImplAdd2UNum( aBuf, nDay ); |
1159 | 717k | aBuf.append( aLocaleDataItem.dateSeparator ); |
1160 | 717k | ImplAddNum( aBuf, nYear, nYearLen ); |
1161 | 717k | break; |
1162 | 0 | default: |
1163 | 0 | ImplAddNum( aBuf, nYear, nYearLen ); |
1164 | 0 | aBuf.append( aLocaleDataItem.dateSeparator ); |
1165 | 0 | ImplAdd2UNum( aBuf, nMonth ); |
1166 | 0 | aBuf.append( aLocaleDataItem.dateSeparator ); |
1167 | 0 | ImplAdd2UNum( aBuf, nDay ); |
1168 | 717k | } |
1169 | | |
1170 | 717k | return aBuf.makeStringAndClear(); |
1171 | 717k | } |
1172 | | |
1173 | | OUString LocaleDataWrapper::getTime( const tools::Time& rTime, bool bSec, bool b100Sec ) const |
1174 | 1.20M | { |
1175 | | //!TODO: leading zeros et al |
1176 | 1.20M | OUStringBuffer aBuf(128); |
1177 | 1.20M | sal_uInt16 nHour = rTime.GetHour(); |
1178 | | |
1179 | 1.20M | nHour %= 24; |
1180 | | |
1181 | 1.20M | ImplAdd2UNum( aBuf, nHour ); |
1182 | 1.20M | aBuf.append( aLocaleDataItem.timeSeparator ); |
1183 | 1.20M | ImplAdd2UNum( aBuf, rTime.GetMin() ); |
1184 | 1.20M | if ( bSec ) |
1185 | 1.20M | { |
1186 | 1.20M | aBuf.append( aLocaleDataItem.timeSeparator ); |
1187 | 1.20M | ImplAdd2UNum( aBuf, rTime.GetSec() ); |
1188 | | |
1189 | 1.20M | if ( b100Sec ) |
1190 | 0 | { |
1191 | 0 | aBuf.append( aLocaleDataItem.time100SecSeparator ); |
1192 | 0 | ImplAdd9UNum( aBuf, rTime.GetNanoSec() ); |
1193 | 0 | } |
1194 | 1.20M | } |
1195 | | |
1196 | 1.20M | return aBuf.makeStringAndClear(); |
1197 | 1.20M | } |
1198 | | |
1199 | | OUString LocaleDataWrapper::getDuration( const tools::Duration& rDuration, bool bSec, bool b100Sec ) const |
1200 | 0 | { |
1201 | 0 | OUStringBuffer aBuf(128); |
1202 | |
|
1203 | 0 | if ( rDuration.IsNegative() ) |
1204 | 0 | aBuf.append(' '); |
1205 | |
|
1206 | 0 | sal_Int64 nHours = static_cast<sal_Int64>(rDuration.GetDays()) * 24 + |
1207 | 0 | (rDuration.IsNegative() ? |
1208 | 0 | -static_cast<sal_Int64>(rDuration.GetTime().GetHour()) : |
1209 | 0 | rDuration.GetTime().GetHour()); |
1210 | 0 | if ( (true) /* IsTimeLeadingZero() */ ) |
1211 | 0 | ImplAddNum( aBuf, nHours, 2 ); |
1212 | 0 | else |
1213 | 0 | ImplAddNum( aBuf, nHours, 1 ); |
1214 | 0 | aBuf.append( aLocaleDataItem.timeSeparator ); |
1215 | 0 | ImplAdd2UNum( aBuf, rDuration.GetTime().GetMin() ); |
1216 | 0 | if ( bSec ) |
1217 | 0 | { |
1218 | 0 | aBuf.append( aLocaleDataItem.timeSeparator ); |
1219 | 0 | ImplAdd2UNum( aBuf, rDuration.GetTime().GetSec() ); |
1220 | |
|
1221 | 0 | if ( b100Sec ) |
1222 | 0 | { |
1223 | 0 | aBuf.append( aLocaleDataItem.time100SecSeparator ); |
1224 | 0 | ImplAdd9UNum( aBuf, rDuration.GetTime().GetNanoSec() ); |
1225 | 0 | } |
1226 | 0 | } |
1227 | |
|
1228 | 0 | return aBuf.makeStringAndClear(); |
1229 | 0 | } |
1230 | | |
1231 | | // --- simple number formatting --------------------------------------- |
1232 | | |
1233 | | static size_t ImplGetNumberStringLengthGuess( const css::i18n::LocaleDataItem2& rLocaleDataItem, sal_uInt16 nDecimals ) |
1234 | 0 | { |
1235 | | // approximately 3.2 bits per digit |
1236 | 0 | const size_t nDig = ((sizeof(sal_Int64) * 8) / 3) + 1; |
1237 | | // digits, separators (pessimized for insane "every digit may be grouped"), leading zero, sign |
1238 | 0 | size_t nGuess = ((nDecimals < nDig) ? |
1239 | 0 | (((nDig - nDecimals) * rLocaleDataItem.thousandSeparator.getLength()) + nDig) : |
1240 | 0 | nDecimals) + rLocaleDataItem.decimalSeparator.getLength() + 3; |
1241 | 0 | return nGuess; |
1242 | 0 | } |
1243 | | |
1244 | | OUString LocaleDataWrapper::getNum( sal_Int64 nNumber, sal_uInt16 nDecimals, |
1245 | | bool bUseThousandSep, bool bTrailingZeros ) const |
1246 | 0 | { |
1247 | | // check if digits and separators will fit into fixed buffer or allocate |
1248 | 0 | size_t nGuess = ImplGetNumberStringLengthGuess( aLocaleDataItem, nDecimals ); |
1249 | 0 | OUStringBuffer aBuf(int(nGuess + 16)); |
1250 | |
|
1251 | 0 | ImplAddFormatNum( aBuf, nNumber, nDecimals, |
1252 | 0 | bUseThousandSep, bTrailingZeros ); |
1253 | |
|
1254 | 0 | return aBuf.makeStringAndClear(); |
1255 | 0 | } |
1256 | | |
1257 | | OUString LocaleDataWrapper::getCurr( sal_Int64 nNumber, sal_uInt16 nDecimals, |
1258 | | std::u16string_view rCurrencySymbol, bool bUseThousandSep ) const |
1259 | 0 | { |
1260 | 0 | sal_Unicode cZeroChar = getCurrZeroChar(); |
1261 | | |
1262 | | // check if digits and separators will fit into fixed buffer or allocate |
1263 | 0 | size_t nGuess = ImplGetNumberStringLengthGuess( aLocaleDataItem, nDecimals ); |
1264 | 0 | OUStringBuffer aNumBuf(sal_Int32(nGuess + 16)); |
1265 | |
|
1266 | 0 | bool bNeg; |
1267 | 0 | if ( nNumber < 0 ) |
1268 | 0 | { |
1269 | 0 | bNeg = true; |
1270 | 0 | nNumber *= -1; |
1271 | 0 | } |
1272 | 0 | else |
1273 | 0 | bNeg = false; |
1274 | | |
1275 | | // convert number |
1276 | 0 | ImplAddFormatNum( aNumBuf, nNumber, nDecimals, |
1277 | 0 | bUseThousandSep, true ); |
1278 | 0 | const sal_Int32 nNumLen = aNumBuf.getLength(); |
1279 | | |
1280 | | // replace zeros with zero character |
1281 | 0 | if ( (cZeroChar != '0') && nDecimals /* && IsNumTrailingZeros() */ ) |
1282 | 0 | { |
1283 | 0 | sal_uInt16 i; |
1284 | 0 | bool bZero = true; |
1285 | |
|
1286 | 0 | sal_uInt16 nNumBufIndex = nNumLen-nDecimals; |
1287 | 0 | i = 0; |
1288 | 0 | do |
1289 | 0 | { |
1290 | 0 | if ( aNumBuf[nNumBufIndex] != '0' ) |
1291 | 0 | { |
1292 | 0 | bZero = false; |
1293 | 0 | break; |
1294 | 0 | } |
1295 | | |
1296 | 0 | nNumBufIndex++; |
1297 | 0 | i++; |
1298 | 0 | } |
1299 | 0 | while ( i < nDecimals ); |
1300 | |
|
1301 | 0 | if ( bZero ) |
1302 | 0 | { |
1303 | 0 | nNumBufIndex = nNumLen-nDecimals; |
1304 | 0 | i = 0; |
1305 | 0 | do |
1306 | 0 | { |
1307 | 0 | aNumBuf[nNumBufIndex] = cZeroChar; |
1308 | 0 | nNumBufIndex++; |
1309 | 0 | i++; |
1310 | 0 | } |
1311 | 0 | while ( i < nDecimals ); |
1312 | 0 | } |
1313 | 0 | } |
1314 | |
|
1315 | 0 | OUString aCur; |
1316 | 0 | if ( !bNeg ) |
1317 | 0 | { |
1318 | 0 | switch( getCurrPositiveFormat() ) |
1319 | 0 | { |
1320 | 0 | case 0: |
1321 | 0 | aCur = rCurrencySymbol + aNumBuf; |
1322 | 0 | break; |
1323 | 0 | case 1: |
1324 | 0 | aCur = aNumBuf + rCurrencySymbol; |
1325 | 0 | break; |
1326 | 0 | case 2: |
1327 | 0 | aCur = OUString::Concat(rCurrencySymbol) + " " + aNumBuf; |
1328 | 0 | break; |
1329 | 0 | case 3: |
1330 | 0 | aCur = aNumBuf + " " + rCurrencySymbol; |
1331 | 0 | break; |
1332 | 0 | } |
1333 | 0 | } |
1334 | 0 | else |
1335 | 0 | { |
1336 | 0 | switch( getCurrNegativeFormat() ) |
1337 | 0 | { |
1338 | 0 | case 0: |
1339 | 0 | aCur = OUString::Concat("(") + rCurrencySymbol + aNumBuf + ")"; |
1340 | 0 | break; |
1341 | 0 | case 1: |
1342 | 0 | aCur = OUString::Concat("-") + rCurrencySymbol + aNumBuf; |
1343 | 0 | break; |
1344 | 0 | case 2: |
1345 | 0 | aCur = OUString::Concat(rCurrencySymbol) + "-" + aNumBuf; |
1346 | 0 | break; |
1347 | 0 | case 3: |
1348 | 0 | aCur = rCurrencySymbol + aNumBuf + "-"; |
1349 | 0 | break; |
1350 | 0 | case 4: |
1351 | 0 | aCur = "(" + aNumBuf + rCurrencySymbol + ")"; |
1352 | 0 | break; |
1353 | 0 | case 5: |
1354 | 0 | aCur = "-" + aNumBuf + rCurrencySymbol; |
1355 | 0 | break; |
1356 | 0 | case 6: |
1357 | 0 | aCur = aNumBuf + "-" + rCurrencySymbol; |
1358 | 0 | break; |
1359 | 0 | case 7: |
1360 | 0 | aCur = aNumBuf + rCurrencySymbol + "-"; |
1361 | 0 | break; |
1362 | 0 | case 8: |
1363 | 0 | aCur = "-" + aNumBuf + " " + rCurrencySymbol; |
1364 | 0 | break; |
1365 | 0 | case 9: |
1366 | 0 | aCur = OUString::Concat("-") + rCurrencySymbol + " " + aNumBuf; |
1367 | 0 | break; |
1368 | 0 | case 10: |
1369 | 0 | aCur = aNumBuf + " " + rCurrencySymbol + "-"; |
1370 | 0 | break; |
1371 | 0 | case 11: |
1372 | 0 | aCur = OUString::Concat(rCurrencySymbol) + " -" + aNumBuf; |
1373 | 0 | break; |
1374 | 0 | case 12: |
1375 | 0 | aCur = OUString::Concat(rCurrencySymbol) + " " + aNumBuf + "-"; |
1376 | 0 | break; |
1377 | 0 | case 13: |
1378 | 0 | aCur = aNumBuf + "- " + rCurrencySymbol; |
1379 | 0 | break; |
1380 | 0 | case 14: |
1381 | 0 | aCur = OUString::Concat("(") + rCurrencySymbol + " " + aNumBuf + ")"; |
1382 | 0 | break; |
1383 | 0 | case 15: |
1384 | 0 | aCur = "(" + aNumBuf + " " + rCurrencySymbol + ")"; |
1385 | 0 | break; |
1386 | 0 | } |
1387 | 0 | } |
1388 | | |
1389 | 0 | return aCur; |
1390 | 0 | } |
1391 | | |
1392 | | // --- number parsing ------------------------------------------------- |
1393 | | |
1394 | | double LocaleDataWrapper::stringToDouble( std::u16string_view aString, bool bUseGroupSep, |
1395 | | rtl_math_ConversionStatus* pStatus, sal_Int32* pParseEnd ) const |
1396 | 0 | { |
1397 | 0 | const sal_Unicode* pParseEndChar; |
1398 | 0 | double fValue = stringToDouble(aString.data(), aString.data() + aString.size(), bUseGroupSep, pStatus, &pParseEndChar); |
1399 | 0 | if (pParseEnd) |
1400 | 0 | *pParseEnd = pParseEndChar - aString.data(); |
1401 | 0 | return fValue; |
1402 | 0 | } |
1403 | | |
1404 | | double LocaleDataWrapper::stringToDouble( const sal_Unicode* pBegin, const sal_Unicode* pEnd, bool bUseGroupSep, |
1405 | | rtl_math_ConversionStatus* pStatus, const sal_Unicode** ppParseEnd ) const |
1406 | 833 | { |
1407 | 833 | const sal_Unicode cGroupSep = (bUseGroupSep ? aLocaleDataItem.thousandSeparator[0] : 0); |
1408 | 833 | rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok; |
1409 | 833 | const sal_Unicode* pParseEnd = nullptr; |
1410 | 833 | double fValue = rtl_math_uStringToDouble( pBegin, pEnd, aLocaleDataItem.decimalSeparator[0], cGroupSep, &eStatus, &pParseEnd); |
1411 | 833 | bool bTryAlt = (pParseEnd < pEnd && !aLocaleDataItem.decimalSeparatorAlternative.isEmpty() && |
1412 | 0 | *pParseEnd == aLocaleDataItem.decimalSeparatorAlternative.toChar()); |
1413 | | // Try re-parsing with alternative if that was the reason to stop. |
1414 | 833 | if (bTryAlt) |
1415 | 0 | fValue = rtl_math_uStringToDouble( pBegin, pEnd, aLocaleDataItem.decimalSeparatorAlternative.toChar(), cGroupSep, &eStatus, &pParseEnd); |
1416 | 833 | if (pStatus) |
1417 | 833 | *pStatus = eStatus; |
1418 | 833 | if (ppParseEnd) |
1419 | 833 | *ppParseEnd = pParseEnd; |
1420 | 833 | return fValue; |
1421 | 833 | } |
1422 | | |
1423 | | // --- mixed ---------------------------------------------------------- |
1424 | | |
1425 | | LanguageTag LocaleDataWrapper::getLoadedLanguageTag() const |
1426 | 1.65M | { |
1427 | 1.65M | LanguageCountryInfo aLCInfo = getLanguageCountryInfo(); |
1428 | 1.65M | return LanguageTag( lang::Locale( aLCInfo.Language, aLCInfo.Country, aLCInfo.Variant )); |
1429 | 1.65M | } |
1430 | | |
1431 | | OUString LocaleDataWrapper::appendLocaleInfo(std::u16string_view rDebugMsg) const |
1432 | 0 | { |
1433 | 0 | LanguageTag aLoaded = getLoadedLanguageTag(); |
1434 | 0 | return OUString::Concat(rDebugMsg) + "\n" + maLanguageTag.getBcp47() + " requested\n" |
1435 | 0 | + aLoaded.getBcp47() + " loaded"; |
1436 | 0 | } |
1437 | | |
1438 | | // static |
1439 | | void LocaleDataWrapper::outputCheckMessage( std::u16string_view rMsg ) |
1440 | 0 | { |
1441 | 0 | outputCheckMessage(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8).getStr()); |
1442 | 0 | } |
1443 | | |
1444 | | // static |
1445 | | void LocaleDataWrapper::outputCheckMessage( const char* pStr ) |
1446 | 0 | { |
1447 | 0 | fprintf( stderr, "\n%s\n", pStr); |
1448 | 0 | fflush( stderr); |
1449 | 0 | SAL_WARN("unotools.i18n", pStr); |
1450 | 0 | } |
1451 | | |
1452 | | // static |
1453 | | void LocaleDataWrapper::evaluateLocaleDataChecking() |
1454 | 31 | { |
1455 | | // Using the rtl_Instance template here wouldn't solve all threaded write |
1456 | | // accesses, since we want to assign the result to the static member |
1457 | | // variable and would need to dereference the pointer returned and assign |
1458 | | // the value unguarded. This is the same pattern manually coded. |
1459 | 31 | sal_uInt8 nCheck = nLocaleDataChecking; |
1460 | 31 | if (!nCheck) |
1461 | 31 | { |
1462 | 31 | ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex()); |
1463 | 31 | nCheck = nLocaleDataChecking; |
1464 | 31 | if (!nCheck) |
1465 | 31 | { |
1466 | | #ifdef DBG_UTIL |
1467 | | nCheck = 1; |
1468 | | #else |
1469 | 31 | const char* pEnv = getenv( "OOO_ENABLE_LOCALE_DATA_CHECKS"); |
1470 | 31 | if (pEnv && (pEnv[0] == 'Y' || pEnv[0] == 'y' || pEnv[0] == '1')) |
1471 | 0 | nCheck = 1; |
1472 | 31 | else |
1473 | 31 | nCheck = 2; |
1474 | 31 | #endif |
1475 | 31 | OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); |
1476 | 31 | nLocaleDataChecking = nCheck; |
1477 | 31 | } |
1478 | 31 | } |
1479 | 0 | else { |
1480 | 0 | OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); |
1481 | 0 | } |
1482 | 31 | } |
1483 | | |
1484 | | // --- XLocaleData3 ---------------------------------------------------------- |
1485 | | |
1486 | | css::uno::Sequence< css::i18n::Calendar2 > LocaleDataWrapper::getAllCalendars() const |
1487 | 8.83k | { |
1488 | 8.83k | try |
1489 | 8.83k | { |
1490 | 8.83k | return xLD->getAllCalendars2( getMyLocale() ); |
1491 | 8.83k | } |
1492 | 8.83k | catch (const Exception&) |
1493 | 8.83k | { |
1494 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCalendars" ); |
1495 | 0 | } |
1496 | 0 | return {}; |
1497 | 8.83k | } |
1498 | | |
1499 | | // --- XLocaleData4 ---------------------------------------------------------- |
1500 | | |
1501 | | const css::uno::Sequence< OUString > & LocaleDataWrapper::getDateAcceptancePatterns() const |
1502 | 175k | { |
1503 | 175k | return aDateAcceptancePatterns; |
1504 | 175k | } |
1505 | | |
1506 | | // --- Override layer -------------------------------------------------------- |
1507 | | |
1508 | | void LocaleDataWrapper::loadDateAcceptancePatterns( |
1509 | | const std::vector<OUString> & rPatterns ) |
1510 | 8.83k | { |
1511 | 8.83k | if (!aDateAcceptancePatterns.hasElements() || rPatterns.empty()) |
1512 | 8.83k | { |
1513 | 8.83k | try |
1514 | 8.83k | { |
1515 | 8.83k | aDateAcceptancePatterns = xLD->getDateAcceptancePatterns( maLanguageTag.getLocale() ); |
1516 | 8.83k | } |
1517 | 8.83k | catch (const Exception&) |
1518 | 8.83k | { |
1519 | 0 | TOOLS_WARN_EXCEPTION( "unotools.i18n", "setDateAcceptancePatterns" ); |
1520 | 0 | } |
1521 | 8.83k | if (rPatterns.empty()) |
1522 | 8.83k | return; // just a reset |
1523 | 0 | if (!aDateAcceptancePatterns.hasElements()) |
1524 | 0 | { |
1525 | 0 | aDateAcceptancePatterns = comphelper::containerToSequence(rPatterns); |
1526 | 0 | return; |
1527 | 0 | } |
1528 | 0 | } |
1529 | | |
1530 | | // Earlier versions checked for presence of the full date pattern with |
1531 | | // aDateAcceptancePatterns[0] == rPatterns[0] and prepended that if not. |
1532 | | // This lead to confusion if the patterns were intentionally specified |
1533 | | // without, giving entirely a different DMY order, see tdf#150288. |
1534 | | // Not checking this and accepting the given patterns as is may result in |
1535 | | // the user shooting themself in the foot, but we can't have both. |
1536 | 0 | aDateAcceptancePatterns = comphelper::containerToSequence(rPatterns); |
1537 | 0 | } |
1538 | | |
1539 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |