/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/** * Loading LocaleDataWrapper can become expensive because of all the function-symbol lookups required, so * we cache these.
*/ // static const LocaleDataWrapper* LocaleDataWrapper::get(const LanguageTag& aLanguageTag)
{ static std::map<LanguageTag, std::unique_ptr<LocaleDataWrapper>> gCache; static std::mutex gMutex;
std::unique_lock l(gMutex); auto it = gCache.find(aLanguageTag); if (it != gCache.end()) return it->second.get(); auto pNew = new LocaleDataWrapper(comphelper::getProcessComponentContext(), aLanguageTag);
gCache.insert({aLanguageTag, std::unique_ptr<LocaleDataWrapper>(pNew)}); return pNew;
};
/* FIXME-BCP47: locale data should provide a language tag instead that could be
* passed on. */
css::i18n::LanguageCountryInfo LocaleDataWrapper::getLanguageCountryInfo() const
{ try
{ return xLD->getLanguageCountryInfo( getMyLocale() );
} catch (const Exception&)
{
TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLanguageCountryInfo" );
} return css::i18n::LanguageCountryInfo();
}
// static
MeasurementSystem LocaleDataWrapper::mapMeasurementStringToEnum( std::u16string_view rMS )
{ //! TODO: could be cached too if ( o3tl::equalsIgnoreAsciiCase( rMS, u"metric" ) ) return MeasurementSystem::Metric; //! TODO: other measurement systems? => extend enum MeasurementSystem return MeasurementSystem::US;
}
bool LocaleDataWrapper::doesSecondaryCalendarUseEC( std::u16string_view rName ) const
{ if (rName.empty()) returnfalse;
// Check language tag first to avoid loading all calendars of this locale.
LanguageTag aLoaded( getLoadedLanguageTag()); const OUString& aBcp47( aLoaded.getBcp47()); // So far determine only by locale, we know for a few. /* TODO: check date format codes? or add to locale data? */ if ( aBcp47 != "ja-JP" &&
aBcp47 != "lo-LA" &&
aBcp47 != "zh-TW") returnfalse;
if (!xSecondaryCalendar) returnfalse; if (!xSecondaryCalendar->Name.equalsIgnoreAsciiCase( rName)) returnfalse;
LongDateOrder LocaleDataWrapper::scanDateOrderImpl( std::u16string_view rCode ) const
{ // Only some european versions were translated, the ones with different // keyword combinations are: // English DMY, German TMJ, Spanish DMA, French JMA, Italian GMA, // Dutch DMJ, Finnish PKV
// default is English keywords for every other language
size_t nDay = rCode.find('D');
size_t nMonth = rCode.find('M');
size_t nYear = rCode.find('Y'); if (nDay == std::u16string_view::npos || nMonth == std::u16string_view::npos || nYear == std::u16string_view::npos)
{ // This algorithm assumes that all three parts (DMY) are present if (nMonth == std::u16string_view::npos)
{ // only Finnish has something else than 'M' for month
nMonth = rCode.find('K'); if (nMonth != std::u16string_view::npos)
{
nDay = rCode.find('P');
nYear = rCode.find('V');
}
} elseif (nDay == std::u16string_view::npos)
{ // We have a month 'M' if we reach this branch. // Possible languages containing 'M' but no 'D': // German, French, Italian
nDay = rCode.find('T'); // German if (nDay != std::u16string_view::npos)
nYear = rCode.find('J'); else
{
nYear = rCode.find('A'); // French, Italian if (nYear != std::u16string_view::npos)
{
nDay = rCode.find('J'); // French if (nDay == std::u16string_view::npos)
nDay = rCode.find('G'); // Italian
}
}
} else
{ // We have a month 'M' and a day 'D'. // Possible languages containing 'D' and 'M' but not 'Y': // Spanish, Dutch
nYear = rCode.find('A'); // Spanish if (nYear == std::u16string_view::npos)
nYear = rCode.find('J'); // Dutch
} if (nDay == std::u16string_view::npos || nMonth == std::u16string_view::npos || nYear == std::u16string_view::npos)
{ if (areChecksEnabled())
{
outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::scanDateOrder: not all DMY present" ) );
} if (nDay == std::u16string_view::npos)
nDay = rCode.size(); if (nMonth == std::u16string_view::npos)
nMonth = rCode.size(); if (nYear == std::u16string_view::npos)
nYear = rCode.size();
}
} // compare with <= because each position may equal rCode.getLength() if ( nDay <= nMonth && nMonth <= nYear ) return LongDateOrder::DMY; // also if every position equals rCode.getLength() elseif ( nMonth <= nDay && nDay <= nYear ) return LongDateOrder::MDY; elseif ( nYear <= nMonth && nMonth <= nDay ) return LongDateOrder::YMD; elseif ( nYear <= nDay && nDay <= nMonth ) return LongDateOrder::YDM; else
{ if (areChecksEnabled())
{
outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::scanDateOrder: no magic applicable" ) );
} return LongDateOrder::DMY;
}
}
static DateOrder getDateOrderFromLongDateOrder( LongDateOrder eLong )
{ switch (eLong)
{ case LongDateOrder::YMD: return DateOrder::YMD; break; case LongDateOrder::DMY: return DateOrder::DMY; break; case LongDateOrder::MDY: return DateOrder::MDY; break; case LongDateOrder::YDM: default:
assert(!"unhandled LongDateOrder to DateOrder"); return DateOrder::DMY;
}
}
void LocaleDataWrapper::loadDateOrders()
{
css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
uno::Sequence< NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( KNumberFormatUsage::DATE, maLanguageTag.getLocale() );
sal_Int32 nCnt = aFormatSeq.getLength(); if ( !nCnt )
{ // bad luck if (areChecksEnabled())
{
outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no date formats" ) );
}
nDateOrder = DateOrder::DMY;
nLongDateOrder = LongDateOrder::DMY; return;
} // find the edit (21), a default (medium preferred), // a medium (default preferred), and a long (default preferred)
NumberFormatCode const * const pFormatArr = aFormatSeq.getArray();
sal_Int32 nEdit, nDef, nMedium, nLong;
nEdit = nDef = nMedium = nLong = -1; for ( sal_Int32 nElem = 0; nElem < nCnt; nElem++ )
{ if ( nEdit == -1 && pFormatArr[nElem].Index == NumberFormatIndex::DATE_SYS_DDMMYYYY )
nEdit = nElem; if ( nDef == -1 && pFormatArr[nElem].Default )
nDef = nElem; switch ( pFormatArr[nElem].Type )
{ case KNumberFormatType::MEDIUM :
{ if ( pFormatArr[nElem].Default )
{
nDef = nElem;
nMedium = nElem;
} elseif ( nMedium == -1 )
nMedium = nElem;
} break; case KNumberFormatType::LONG :
{ if ( pFormatArr[nElem].Default )
nLong = nElem; elseif ( nLong == -1 )
nLong = nElem;
} break;
}
} if ( nEdit == -1 )
{ if (areChecksEnabled())
{
outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no edit" ) );
} if ( nDef == -1 )
{ if (areChecksEnabled())
{
outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no default" ) );
} if ( nMedium != -1 )
nDef = nMedium; elseif ( nLong != -1 )
nDef = nLong; else
nDef = 0;
}
nEdit = nDef;
}
LongDateOrder nDO = scanDateOrderImpl( pFormatArr[nEdit].Code ); if ( pFormatArr[nEdit].Type == KNumberFormatType::LONG )
{ // normally this is not the case
nLongDateOrder = nDO;
nDateOrder = getDateOrderFromLongDateOrder(nDO);
} else
{ // YDM should not occur in a short/medium date (i.e. no locale has // that) and is nowhere handled.
nDateOrder = getDateOrderFromLongDateOrder(nDO); if ( nLong == -1 )
nLongDateOrder = nDO; else
nLongDateOrder = scanDateOrderImpl( pFormatArr[nLong].Code );
}
}
void LocaleDataWrapper::loadDigitGrouping()
{ /* TODO: This is a very simplified grouping setup that only serves its * current purpose for Indian locales. A free-form flexible one would * obtain grouping from locale data where it could be specified using, for * example, codes like #,### and #,##,### that would generate the integer * sequence. Needed additional API and a locale data element.
*/
if (aGrouping.hasElements() && aGrouping[0]) return;
// check if digits and separators will fit into fixed buffer or allocate
size_t nGuess = ImplGetNumberStringLengthGuess( aLocaleDataItem, nDecimals );
OUStringBuffer aNumBuf(sal_Int32(nGuess + 16));
// static void LocaleDataWrapper::evaluateLocaleDataChecking()
{ // Using the rtl_Instance template here wouldn't solve all threaded write // accesses, since we want to assign the result to the static member // variable and would need to dereference the pointer returned and assign // the value unguarded. This is the same pattern manually coded.
sal_uInt8 nCheck = nLocaleDataChecking; if (!nCheck)
{
::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex());
nCheck = nLocaleDataChecking; if (!nCheck)
{ #ifdef DBG_UTIL
nCheck = 1; #else constchar* pEnv = getenv( "OOO_ENABLE_LOCALE_DATA_CHECKS"); if (pEnv && (pEnv[0] == 'Y' || pEnv[0] == 'y' || pEnv[0] == '1'))
nCheck = 1; else
nCheck = 2; #endif
OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
nLocaleDataChecking = nCheck;
}
} else {
OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
}
}
void LocaleDataWrapper::loadDateAcceptancePatterns( const std::vector<OUString> & rPatterns )
{ if (!aDateAcceptancePatterns.hasElements() || rPatterns.empty())
{ try
{
aDateAcceptancePatterns = xLD->getDateAcceptancePatterns( maLanguageTag.getLocale() );
} catch (const Exception&)
{
TOOLS_WARN_EXCEPTION( "unotools.i18n", "setDateAcceptancePatterns" );
} if (rPatterns.empty()) return; // just a reset if (!aDateAcceptancePatterns.hasElements())
{
aDateAcceptancePatterns = comphelper::containerToSequence(rPatterns); return;
}
}
// Earlier versions checked for presence of the full date pattern with // aDateAcceptancePatterns[0] == rPatterns[0] and prepended that if not. // This lead to confusion if the patterns were intentionally specified // without, giving entirely a different DMY order, see tdf#150288. // Not checking this and accepting the given patterns as is may result in // the user shooting themself in the foot, but we can't have both.
aDateAcceptancePatterns = comphelper::containerToSequence(rPatterns);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.