summaryrefslogtreecommitdiffstats
path: root/unotools/source/i18n
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /unotools/source/i18n
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'unotools/source/i18n')
-rw-r--r--unotools/source/i18n/calendarwrapper.cxx341
-rw-r--r--unotools/source/i18n/caserotate.cxx43
-rw-r--r--unotools/source/i18n/charclass.cxx456
-rw-r--r--unotools/source/i18n/collatorwrapper.cxx97
-rw-r--r--unotools/source/i18n/intlwrapper.cxx59
-rw-r--r--unotools/source/i18n/localedatawrapper.cxx1826
-rw-r--r--unotools/source/i18n/nativenumberwrapper.cxx110
-rw-r--r--unotools/source/i18n/readwritemutexguard.cxx109
-rw-r--r--unotools/source/i18n/resmgr.cxx280
-rw-r--r--unotools/source/i18n/textsearch.cxx401
-rw-r--r--unotools/source/i18n/transliterationwrapper.cxx232
11 files changed, 3954 insertions, 0 deletions
diff --git a/unotools/source/i18n/calendarwrapper.cxx b/unotools/source/i18n/calendarwrapper.cxx
new file mode 100644
index 000000000..5f4a1669b
--- /dev/null
+++ b/unotools/source/i18n/calendarwrapper.cxx
@@ -0,0 +1,341 @@
+/* -*- 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 .
+ */
+
+#include <sal/log.hxx>
+#include <unotools/calendarwrapper.hxx>
+#include <com/sun/star/i18n/LocaleCalendar2.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+
+CalendarWrapper::CalendarWrapper(
+ const Reference< uno::XComponentContext > & rxContext
+ )
+ :
+ aEpochStart( Date( 1, 1, 1970 ) )
+{
+ xC = LocaleCalendar2::create(rxContext);
+}
+
+CalendarWrapper::~CalendarWrapper()
+{
+}
+
+void CalendarWrapper::loadDefaultCalendar( const css::lang::Locale& rLocale, bool bTimeZoneUTC )
+{
+ try
+ {
+ if ( xC.is() )
+ xC->loadDefaultCalendarTZ( rLocale, (bTimeZoneUTC ? "UTC" : OUString()));
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "loadDefaultCalendar" );
+ }
+}
+
+void CalendarWrapper::loadCalendar( const OUString& rUniqueID, const css::lang::Locale& rLocale, bool bTimeZoneUTC )
+{
+ try
+ {
+ if ( xC.is() )
+ xC->loadCalendarTZ( rUniqueID, rLocale, (bTimeZoneUTC ? "UTC" : OUString()));
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "loadCalendar: "
+ << rUniqueID << " Locale: " << rLocale.Language << "_" << rLocale.Country );
+ }
+}
+
+css::uno::Sequence< OUString > CalendarWrapper::getAllCalendars( const css::lang::Locale& rLocale ) const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getAllCalendars( rLocale );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCalendars" );
+ }
+
+ return css::uno::Sequence< OUString > (0);
+}
+
+OUString CalendarWrapper::getUniqueID() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getUniqueID();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getUniqueID" );
+ }
+ return OUString();
+}
+
+void CalendarWrapper::setDateTime( double fTimeInDays )
+{
+ try
+ {
+ if ( xC.is() )
+ xC->setDateTime( fTimeInDays );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "setDateTime" );
+ }
+}
+
+double CalendarWrapper::getDateTime() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getDateTime();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDateTime" );
+ }
+ return 0.0;
+}
+
+void CalendarWrapper::setLocalDateTime( double fTimeInDays )
+{
+ try
+ {
+ if ( xC.is() )
+ {
+ xC->setLocalDateTime( fTimeInDays );
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "setLocalDateTime" );
+ }
+}
+
+double CalendarWrapper::getLocalDateTime() const
+{
+ try
+ {
+ if ( xC.is() )
+ {
+ return xC->getLocalDateTime();
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLocalDateTime" );
+ }
+ return 0.0;
+}
+
+void CalendarWrapper::setValue( sal_Int16 nFieldIndex, sal_Int16 nValue )
+{
+ try
+ {
+ if ( xC.is() )
+ xC->setValue( nFieldIndex, nValue );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "setValue" );
+ }
+}
+
+bool CalendarWrapper::isValid() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->isValid();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "isValid" );
+ }
+ return false;
+}
+
+sal_Int16 CalendarWrapper::getValue( sal_Int16 nFieldIndex ) const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getValue( nFieldIndex );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getValue" );
+ }
+ return 0;
+}
+
+sal_Int16 CalendarWrapper::getFirstDayOfWeek() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getFirstDayOfWeek();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getFirstDayOfWeek" );
+ }
+ return 0;
+}
+
+sal_Int16 CalendarWrapper::getNumberOfMonthsInYear() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getNumberOfMonthsInYear();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getNumberOfMonthsInYear" );
+ }
+ return 0;
+}
+
+sal_Int16 CalendarWrapper::getNumberOfDaysInWeek() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getNumberOfDaysInWeek();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getNumberOfDaysInWeek" );
+ }
+ return 0;
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > CalendarWrapper::getMonths() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getMonths2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getMonths" );
+ }
+ return css::uno::Sequence< css::i18n::CalendarItem2 > (0);
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > CalendarWrapper::getDays() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getDays2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDays" );
+ }
+ return css::uno::Sequence< css::i18n::CalendarItem2 > (0);
+}
+
+OUString CalendarWrapper::getDisplayName( sal_Int16 nCalendarDisplayIndex, sal_Int16 nIdx, sal_Int16 nNameType ) const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getDisplayName( nCalendarDisplayIndex, nIdx, nNameType );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDisplayName" );
+ }
+ return OUString();
+}
+
+// --- XExtendedCalendar -----------------------------------------------------
+
+OUString CalendarWrapper::getDisplayString( sal_Int32 nCalendarDisplayCode, sal_Int16 nNativeNumberMode ) const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getDisplayString( nCalendarDisplayCode, nNativeNumberMode );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDisplayString" );
+ }
+ return OUString();
+}
+
+// --- XCalendar3 ------------------------------------------------------------
+
+css::i18n::Calendar2 CalendarWrapper::getLoadedCalendar() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getLoadedCalendar2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLoadedCalendar2" );
+ }
+ return css::i18n::Calendar2();
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > CalendarWrapper::getGenitiveMonths() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getGenitiveMonths2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getGenitiveMonths" );
+ }
+ return css::uno::Sequence< css::i18n::CalendarItem2 > (0);
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > CalendarWrapper::getPartitiveMonths() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getPartitiveMonths2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getPartitiveMonths" );
+ }
+ return css::uno::Sequence< css::i18n::CalendarItem2 > (0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/caserotate.cxx b/unotools/source/i18n/caserotate.cxx
new file mode 100644
index 000000000..a242b855e
--- /dev/null
+++ b/unotools/source/i18n/caserotate.cxx
@@ -0,0 +1,43 @@
+/* -*- 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/.
+ */
+
+#include <unotools/caserotate.hxx>
+#include <i18nutil/transliteration.hxx>
+
+//TODO Use XCharacterClassification::getStringType to determine the current
+//(possibly mixed) case type and rotate to the next one
+
+TransliterationFlags RotateTransliteration::getNextMode()
+{
+ TransliterationFlags nMode = TransliterationFlags::NONE;
+
+ switch (nF3ShiftCounter)
+ {
+ case 0:
+ nMode = TransliterationFlags::TITLE_CASE;
+ break;
+ case 1: //tdf#116315
+ nMode = TransliterationFlags::SENTENCE_CASE;
+ break;
+ case 2:
+ nMode = TransliterationFlags::LOWERCASE_UPPERCASE;
+ break;
+ default:
+ case 3:
+ nMode = TransliterationFlags::UPPERCASE_LOWERCASE;
+ nF3ShiftCounter = -1;
+ break;
+ }
+
+ nF3ShiftCounter++;
+
+ return nMode;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/charclass.cxx b/unotools/source/i18n/charclass.cxx
new file mode 100644
index 000000000..faec870ff
--- /dev/null
+++ b/unotools/source/i18n/charclass.cxx
@@ -0,0 +1,456 @@
+/* -*- 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 .
+ */
+
+#include <comphelper/processfactory.hxx>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <rtl/character.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/i18n/CharacterClassification.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+
+CharClass::CharClass(
+ const Reference< uno::XComponentContext > & rxContext,
+ const LanguageTag& rLanguageTag
+ )
+ :
+ maLanguageTag( rLanguageTag)
+{
+ xCC = CharacterClassification::create( rxContext );
+}
+
+CharClass::CharClass(
+ const LanguageTag& rLanguageTag )
+ :
+ maLanguageTag( rLanguageTag)
+{
+ xCC = CharacterClassification::create( comphelper::getProcessComponentContext() );
+}
+
+CharClass::~CharClass()
+{
+}
+
+void CharClass::setLanguageTag( const LanguageTag& rLanguageTag )
+{
+ ::osl::MutexGuard aGuard( aMutex );
+ maLanguageTag = rLanguageTag;
+}
+
+const LanguageTag& CharClass::getLanguageTag() const
+{
+ ::osl::MutexGuard aGuard( aMutex );
+ return maLanguageTag;
+}
+
+const css::lang::Locale& CharClass::getMyLocale() const
+{
+ // Mutex locked by callers.
+ return maLanguageTag.getLocale();
+}
+
+// static
+bool CharClass::isAsciiNumeric( const OUString& rStr )
+{
+ if ( rStr.isEmpty() )
+ return false;
+ const sal_Unicode* p = rStr.getStr();
+ const sal_Unicode* const pStop = p + rStr.getLength();
+
+ do
+ {
+ if ( !rtl::isAsciiDigit( *p ) )
+ return false;
+ }
+ while ( ++p < pStop );
+
+ return true;
+}
+
+// static
+bool CharClass::isAsciiAlpha( const OUString& rStr )
+{
+ if ( rStr.isEmpty() )
+ return false;
+ const sal_Unicode* p = rStr.getStr();
+ const sal_Unicode* const pStop = p + rStr.getLength();
+
+ do
+ {
+ if ( !rtl::isAsciiAlpha( *p ) )
+ return false;
+ }
+ while ( ++p < pStop );
+
+ return true;
+}
+
+bool CharClass::isAlpha( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiAlpha( c );
+
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ nCharClassAlphaType) != 0;
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "isAlpha: Exception caught!" );
+ }
+ return false;
+}
+
+bool CharClass::isLetter( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiAlpha( c );
+
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ nCharClassLetterType) != 0;
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "isLetter: Exception caught!" );
+ }
+ return false;
+}
+
+bool CharClass::isLetter( const OUString& rStr ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return isLetterType( xCC->getStringType( rStr, 0, rStr.getLength(), getMyLocale() ) );
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "isLetter: Exception caught!" );
+ }
+ return false;
+}
+
+bool CharClass::isDigit( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[ nPos ];
+ if ( c < 128 )
+ return rtl::isAsciiDigit( c );
+
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ KCharacterType::DIGIT) != 0;
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "isDigit: Exception caught!" );
+ }
+ return false;
+}
+
+bool CharClass::isNumeric( const OUString& rStr ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return isNumericType( xCC->getStringType( rStr, 0, rStr.getLength(), getMyLocale() ) );
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "isNumeric: Exception caught!" );
+ }
+ return false;
+}
+
+bool CharClass::isAlphaNumeric( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiAlphanumeric( c );
+
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ (nCharClassAlphaType | KCharacterType::DIGIT)) != 0;
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "isAlphaNumeric: Exception caught!" );
+ }
+ return false;
+}
+
+bool CharClass::isLetterNumeric( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiAlphanumeric( c );
+
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ (nCharClassLetterType | KCharacterType::DIGIT)) != 0;
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "isLetterNumeric: Exception caught!" );
+ }
+ return false;
+}
+
+bool CharClass::isLetterNumeric( const OUString& rStr ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return isLetterNumericType( xCC->getStringType( rStr, 0, rStr.getLength(), getMyLocale() ) );
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "isLetterNumeric: Exception caught!" );
+ }
+ return false;
+}
+
+OUString CharClass::titlecase(const OUString& rStr, sal_Int32 nPos, sal_Int32 nCount) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return xCC->toTitle( rStr, nPos, nCount, getMyLocale() );
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "titlecase: Exception caught!" );
+ }
+ return rStr.copy( nPos, nCount );
+}
+
+OUString CharClass::uppercase( const OUString& rStr, sal_Int32 nPos, sal_Int32 nCount ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return xCC->toUpper( rStr, nPos, nCount, getMyLocale() );
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "uppercase: Exception caught!" );
+ }
+ return rStr.copy( nPos, nCount );
+}
+
+OUString CharClass::lowercase( const OUString& rStr, sal_Int32 nPos, sal_Int32 nCount ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return xCC->toLower( rStr, nPos, nCount, getMyLocale() );
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "lowercase: Exception caught!" );
+ }
+ return rStr.copy( nPos, nCount );
+}
+
+sal_Int16 CharClass::getType( const OUString& rStr, sal_Int32 nPos ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return xCC->getType( rStr, nPos );
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "getType: Exception caught!" );
+ }
+ return 0;
+}
+
+css::i18n::DirectionProperty CharClass::getCharacterDirection( const OUString& rStr, sal_Int32 nPos ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return static_cast<css::i18n::DirectionProperty>(xCC->getCharacterDirection( rStr, nPos ));
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "getCharacterDirection: Exception caught!" );
+ }
+ return css::i18n::DirectionProperty_LEFT_TO_RIGHT;
+}
+
+css::i18n::UnicodeScript CharClass::getScript( const OUString& rStr, sal_Int32 nPos ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return static_cast<css::i18n::UnicodeScript>(xCC->getScript( rStr, nPos ));
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "getScript: Exception caught!" );
+ }
+ return UnicodeScript_kBasicLatin;
+}
+
+sal_Int32 CharClass::getCharacterType( const OUString& rStr, sal_Int32 nPos ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return xCC->getCharacterType( rStr, nPos, getMyLocale() );
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "getCharacterType: Exception caught!" );
+ }
+ return 0;
+}
+
+sal_Int32 CharClass::getStringType( const OUString& rStr, sal_Int32 nPos, sal_Int32 nCount ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return xCC->getStringType( rStr, nPos, nCount, getMyLocale() );
+ }
+ }
+ catch ( const Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "getStringType: Exception caught!" );
+ }
+ return 0;
+}
+
+css::i18n::ParseResult CharClass::parseAnyToken(
+ const OUString& rStr,
+ sal_Int32 nPos,
+ sal_Int32 nStartCharFlags,
+ const OUString& userDefinedCharactersStart,
+ sal_Int32 nContCharFlags,
+ const OUString& userDefinedCharactersCont ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return xCC->parseAnyToken( rStr, nPos, getMyLocale(),
+ nStartCharFlags, userDefinedCharactersStart,
+ nContCharFlags, userDefinedCharactersCont );
+ }
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "parseAnyToken" );
+ }
+ return ParseResult();
+}
+
+css::i18n::ParseResult CharClass::parsePredefinedToken(
+ sal_Int32 nTokenType,
+ const OUString& rStr,
+ sal_Int32 nPos,
+ sal_Int32 nStartCharFlags,
+ const OUString& userDefinedCharactersStart,
+ sal_Int32 nContCharFlags,
+ const OUString& userDefinedCharactersCont ) const
+{
+ try
+ {
+ if ( xCC.is() )
+ {
+ ::osl::MutexGuard aGuard( aMutex );
+ return xCC->parsePredefinedToken( nTokenType, rStr, nPos, getMyLocale(),
+ nStartCharFlags, userDefinedCharactersStart,
+ nContCharFlags, userDefinedCharactersCont );
+ }
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "parsePredefinedToken" );
+ }
+ return ParseResult();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/collatorwrapper.cxx b/unotools/source/i18n/collatorwrapper.cxx
new file mode 100644
index 000000000..4da1398e0
--- /dev/null
+++ b/unotools/source/i18n/collatorwrapper.cxx
@@ -0,0 +1,97 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <com/sun/star/i18n/Collator.hpp>
+
+using namespace ::com::sun::star;
+
+CollatorWrapper::CollatorWrapper ( const uno::Reference< uno::XComponentContext > &rxContext )
+{
+ mxInternationalCollator = i18n::Collator::create( rxContext );
+}
+
+sal_Int32
+CollatorWrapper::compareString (const OUString& s1, const OUString& s2) const
+{
+ try
+ {
+ if (mxInternationalCollator.is())
+ return mxInternationalCollator->compareString (s1, s2);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ SAL_WARN( "unotools.i18n","CollatorWrapper: compareString failed");
+ }
+
+ return 0;
+}
+
+uno::Sequence< OUString >
+CollatorWrapper::listCollatorAlgorithms (const lang::Locale& rLocale) const
+{
+ try
+ {
+ if (mxInternationalCollator.is())
+ return mxInternationalCollator->listCollatorAlgorithms (rLocale);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ SAL_WARN( "unotools.i18n","CollatorWrapper: listCollatorAlgorithms failed");
+ }
+
+ return uno::Sequence< OUString > ();
+}
+
+sal_Int32
+CollatorWrapper::loadDefaultCollator (const lang::Locale& rLocale, sal_Int32 nOptions)
+{
+ try
+ {
+ if (mxInternationalCollator.is())
+ return mxInternationalCollator->loadDefaultCollator (rLocale, nOptions);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ SAL_WARN( "unotools.i18n","CollatorWrapper: loadDefaultCollator failed");
+ }
+
+ return 0;
+}
+
+void
+CollatorWrapper::loadCollatorAlgorithm (const OUString& rAlgorithm,
+ const lang::Locale& rLocale, sal_Int32 nOptions)
+{
+ try
+ {
+ if (mxInternationalCollator.is())
+ mxInternationalCollator->loadCollatorAlgorithm (
+ rAlgorithm, rLocale, nOptions);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ SAL_WARN( "unotools.i18n","CollatorWrapper: loadCollatorAlgorithm failed");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/intlwrapper.cxx b/unotools/source/i18n/intlwrapper.cxx
new file mode 100644
index 000000000..f15869fb6
--- /dev/null
+++ b/unotools/source/i18n/intlwrapper.cxx
@@ -0,0 +1,59 @@
+/* -*- 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 .
+ */
+
+#include <unotools/intlwrapper.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <com/sun/star/i18n/CollatorOptions.hpp>
+#include <comphelper/processfactory.hxx>
+
+IntlWrapper::IntlWrapper(
+ const LanguageTag& rLanguageTag )
+ :
+ maLanguageTag( rLanguageTag ),
+ m_xContext( comphelper::getProcessComponentContext() )
+{
+}
+
+IntlWrapper::~IntlWrapper()
+{
+}
+
+void IntlWrapper::ImplNewLocaleData() const
+{
+ const_cast<IntlWrapper*>(this)->pLocaleData.reset( new LocaleDataWrapper( m_xContext, maLanguageTag ) );
+}
+
+void IntlWrapper::ImplNewCollator( bool bCaseSensitive ) const
+{
+ CollatorWrapper* p = new CollatorWrapper( m_xContext );
+ if ( bCaseSensitive )
+ {
+ p->loadDefaultCollator( maLanguageTag.getLocale(), 0 );
+ const_cast<IntlWrapper*>(this)->pCaseCollator.reset(p);
+ }
+ else
+ {
+ p->loadDefaultCollator( maLanguageTag.getLocale(),
+ css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
+ const_cast<IntlWrapper*>(this)->pCollator.reset(p);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/localedatawrapper.cxx b/unotools/source/i18n/localedatawrapper.cxx
new file mode 100644
index 000000000..f9ade4996
--- /dev/null
+++ b/unotools/source/i18n/localedatawrapper.cxx
@@ -0,0 +1,1826 @@
+/* -*- 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 .
+ */
+
+#include <stdio.h>
+#include <string>
+
+#include <sal/log.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/calendarwrapper.hxx>
+#include <unotools/digitgroupingiterator.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
+#include <com/sun/star/i18n/KNumberFormatType.hpp>
+#include <com/sun/star/i18n/LocaleData2.hpp>
+#include <com/sun/star/i18n/CalendarFieldIndex.hpp>
+#include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
+#include <com/sun/star/i18n/NumberFormatIndex.hpp>
+#include <com/sun/star/i18n/NumberFormatMapper.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <rtl/instance.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/math.hxx>
+
+static const sal_uInt16 nCurrFormatInvalid = 0xffff;
+static const sal_uInt16 nCurrFormatDefault = 0;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+
+namespace
+{
+ struct InstalledLocales
+ : public rtl::Static<
+ uno::Sequence< lang::Locale >, InstalledLocales >
+ {};
+
+ struct InstalledLanguageTypes
+ : public rtl::Static<
+ std::vector< LanguageType >, InstalledLanguageTypes >
+ {};
+}
+
+bool LocaleDataWrapper::Locale_Compare::operator()(const css::lang::Locale& rLocale1, const css::lang::Locale& rLocale2) const
+{
+ if (rLocale1.Language < rLocale2.Language)
+ return true;
+ else if (rLocale1.Language > rLocale2.Language)
+ return false;
+
+ if (rLocale1.Country < rLocale2.Country)
+ return true;
+ else if (rLocale1.Country > rLocale2.Country)
+ return false;
+
+ return rLocale1.Variant < rLocale2.Variant;
+}
+
+sal_uInt8 LocaleDataWrapper::nLocaleDataChecking = 0;
+
+LocaleDataWrapper::LocaleDataWrapper(
+ const Reference< uno::XComponentContext > & rxContext,
+ const LanguageTag& rLanguageTag
+ )
+ :
+ m_xContext( rxContext ),
+ xLD( LocaleData2::create(rxContext) ),
+ maLanguageTag( rLanguageTag ),
+ bLocaleDataItemValid( false ),
+ bReservedWordValid( false ),
+ bSecondaryCalendarValid( false )
+{
+ invalidateData();
+}
+
+LocaleDataWrapper::LocaleDataWrapper(
+ const LanguageTag& rLanguageTag
+ )
+ :
+ m_xContext( comphelper::getProcessComponentContext() ),
+ xLD( LocaleData2::create(m_xContext) ),
+ maLanguageTag( rLanguageTag ),
+ bLocaleDataItemValid( false ),
+ bReservedWordValid( false ),
+ bSecondaryCalendarValid( false )
+{
+ invalidateData();
+}
+
+LocaleDataWrapper::~LocaleDataWrapper()
+{
+}
+
+void LocaleDataWrapper::setLanguageTag( const LanguageTag& rLanguageTag )
+{
+ ::utl::ReadWriteGuard aGuard( aMutex, ReadWriteGuardMode::CriticalChange );
+ maLanguageTag = rLanguageTag;
+ invalidateData();
+}
+
+const LanguageTag& LocaleDataWrapper::getLanguageTag() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ return maLanguageTag;
+}
+
+const css::lang::Locale& LocaleDataWrapper::getMyLocale() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ return maLanguageTag.getLocale();
+}
+
+void LocaleDataWrapper::invalidateData()
+{
+ aCurrSymbol.clear();
+ aCurrBankSymbol.clear();
+ nDateOrder = nLongDateOrder = DateOrder::Invalid;
+ nCurrPositiveFormat = nCurrNegativeFormat = nCurrDigits = nCurrFormatInvalid;
+ if ( bLocaleDataItemValid )
+ {
+ for (OUString & j : aLocaleItem)
+ j.clear();
+ bLocaleDataItemValid = false;
+ }
+ if ( bReservedWordValid )
+ {
+ for (OUString & j : aReservedWord)
+ j.clear();
+ bReservedWordValid = false;
+ }
+ xDefaultCalendar.reset();
+ xSecondaryCalendar.reset();
+ bSecondaryCalendarValid = false;
+ if (aGrouping.hasElements())
+ aGrouping[0] = 0;
+ if (aDateAcceptancePatterns.hasElements())
+ aDateAcceptancePatterns = Sequence<OUString>();
+}
+
+/* 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();
+}
+
+const css::i18n::LocaleDataItem2& LocaleDataWrapper::getLocaleItem() const
+{
+ {
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ const css::lang::Locale& rLocal = getMyLocale();
+ auto itr = maDataItemCache.find(rLocal);
+ if (itr != maDataItemCache.end())
+ return itr->second;
+ }
+
+ try
+ {
+ ::utl::ReadWriteGuard aGuard( aMutex );
+
+ const css::lang::Locale& rLocal = getMyLocale();
+ css::i18n::LocaleDataItem2 aItem = xLD->getLocaleItem2( rLocal );
+ auto aRet = maDataItemCache.insert(std::make_pair(rLocal, aItem));
+ assert(aRet.second);
+ return aRet.first->second;
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLocaleItem" );
+ }
+ static css::i18n::LocaleDataItem2 aEmptyItem;
+ return aEmptyItem;
+}
+
+css::uno::Sequence< css::i18n::Currency2 > LocaleDataWrapper::getAllCurrencies() const
+{
+ try
+ {
+ return xLD->getAllCurrencies2( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCurrencies" );
+ }
+ return css::uno::Sequence< css::i18n::Currency2 >(0);
+}
+
+css::uno::Sequence< css::i18n::FormatElement > LocaleDataWrapper::getAllFormats() const
+{
+ try
+ {
+ return xLD->getAllFormats( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllFormats" );
+ }
+ return css::uno::Sequence< css::i18n::FormatElement >(0);
+}
+
+css::i18n::ForbiddenCharacters LocaleDataWrapper::getForbiddenCharacters() const
+{
+ try
+ {
+ return xLD->getForbiddenCharacters( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getForbiddenCharacters" );
+ }
+ return css::i18n::ForbiddenCharacters();
+}
+
+css::uno::Sequence< OUString > LocaleDataWrapper::getReservedWord() const
+{
+ try
+ {
+ return xLD->getReservedWord( getMyLocale() );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getReservedWord" );
+ }
+ return css::uno::Sequence< OUString >(0);
+}
+
+css::uno::Sequence< css::lang::Locale > LocaleDataWrapper::getAllInstalledLocaleNames() const
+{
+ uno::Sequence< lang::Locale > &rInstalledLocales = InstalledLocales::get();
+
+ if ( rInstalledLocales.hasElements() )
+ return rInstalledLocales;
+
+ try
+ {
+ rInstalledLocales = xLD->getAllInstalledLocaleNames();
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllInstalledLocaleNames" );
+ }
+ return rInstalledLocales;
+}
+
+// --- Impl and helpers ----------------------------------------------------
+
+// static
+css::uno::Sequence< css::lang::Locale > LocaleDataWrapper::getInstalledLocaleNames()
+{
+ const uno::Sequence< lang::Locale > &rInstalledLocales =
+ InstalledLocales::get();
+
+ if ( !rInstalledLocales.hasElements() )
+ {
+ LocaleDataWrapper aLDW( ::comphelper::getProcessComponentContext(), LanguageTag( LANGUAGE_SYSTEM) );
+ aLDW.getAllInstalledLocaleNames();
+ }
+ return rInstalledLocales;
+}
+
+// static
+std::vector< LanguageType > LocaleDataWrapper::getInstalledLanguageTypes()
+{
+ std::vector< LanguageType > &rInstalledLanguageTypes =
+ InstalledLanguageTypes::get();
+
+ if ( !rInstalledLanguageTypes.empty() )
+ return rInstalledLanguageTypes;
+
+ const css::uno::Sequence< css::lang::Locale > xLoc = getInstalledLocaleNames();
+ sal_Int32 nCount = xLoc.getLength();
+ std::vector< LanguageType > xLang;
+ xLang.reserve(nCount);
+ for ( const auto& rLoc : xLoc )
+ {
+ LanguageTag aLanguageTag( rLoc );
+ OUString aDebugLocale;
+ if (areChecksEnabled())
+ {
+ aDebugLocale = aLanguageTag.getBcp47( false);
+ }
+
+ LanguageType eLang = aLanguageTag.getLanguageType( false);
+ if (areChecksEnabled() && eLang == LANGUAGE_DONTKNOW)
+ {
+ OUString aMsg = "ConvertIsoNamesToLanguage: unknown MS-LCID for locale\n" +
+ aDebugLocale;
+ outputCheckMessage(aMsg);
+ }
+
+ if ( eLang == LANGUAGE_NORWEGIAN) // no_NO, not Bokmal (nb_NO), not Nynorsk (nn_NO)
+ eLang = LANGUAGE_DONTKNOW; // don't offer "Unknown" language
+ if ( eLang != LANGUAGE_DONTKNOW )
+ {
+ LanguageTag aBackLanguageTag( eLang);
+ if ( aLanguageTag != aBackLanguageTag )
+ {
+ // In checks, exclude known problems because no MS-LCID defined
+ // and default for Language found.
+ if ( areChecksEnabled()
+ && aDebugLocale != "ar-SD" // Sudan/ar
+ && aDebugLocale != "en-CB" // Caribbean is not a country
+// && aDebugLocale != "en-BG" // ?!? Bulgaria/en
+// && aDebugLocale != "es-BR" // ?!? Brazil/es
+ )
+ {
+ OUStringBuffer aMsg("ConvertIsoNamesToLanguage/ConvertLanguageToIsoNames: ambiguous locale (MS-LCID?)\n");
+ aMsg.append(aDebugLocale);
+ aMsg.append(" -> 0x");
+ aMsg.append(static_cast<sal_Int32>(static_cast<sal_uInt16>(eLang)), 16);
+ aMsg.append(" -> ");
+ aMsg.append(aBackLanguageTag.getBcp47());
+ outputCheckMessage( aMsg.makeStringAndClear() );
+ }
+ eLang = LANGUAGE_DONTKNOW;
+ }
+ }
+ if ( eLang != LANGUAGE_DONTKNOW )
+ xLang.push_back(eLang);
+ }
+ rInstalledLanguageTypes = xLang;
+
+ return rInstalledLanguageTypes;
+}
+
+const OUString& LocaleDataWrapper::getOneLocaleItem( sal_Int16 nItem ) const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if ( nItem >= LocaleItem::COUNT2 )
+ {
+ SAL_WARN( "unotools.i18n", "getOneLocaleItem: bounds" );
+ return aLocaleItem[0];
+ }
+ if (aLocaleItem[nItem].isEmpty())
+ { // no cached content
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getOneLocaleItemImpl( nItem );
+ }
+ return aLocaleItem[nItem];
+}
+
+void LocaleDataWrapper::getOneLocaleItemImpl( sal_Int16 nItem )
+{
+ if ( !bLocaleDataItemValid )
+ {
+ aLocaleDataItem = getLocaleItem();
+ bLocaleDataItemValid = true;
+ }
+ switch ( nItem )
+ {
+ case LocaleItem::DATE_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.dateSeparator;
+ break;
+ case LocaleItem::THOUSAND_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.thousandSeparator;
+ break;
+ case LocaleItem::DECIMAL_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.decimalSeparator;
+ break;
+ case LocaleItem::TIME_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.timeSeparator;
+ break;
+ case LocaleItem::TIME_100SEC_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.time100SecSeparator;
+ break;
+ case LocaleItem::LIST_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.listSeparator;
+ break;
+ case LocaleItem::SINGLE_QUOTATION_START :
+ aLocaleItem[nItem] = aLocaleDataItem.quotationStart;
+ break;
+ case LocaleItem::SINGLE_QUOTATION_END :
+ aLocaleItem[nItem] = aLocaleDataItem.quotationEnd;
+ break;
+ case LocaleItem::DOUBLE_QUOTATION_START :
+ aLocaleItem[nItem] = aLocaleDataItem.doubleQuotationStart;
+ break;
+ case LocaleItem::DOUBLE_QUOTATION_END :
+ aLocaleItem[nItem] = aLocaleDataItem.doubleQuotationEnd;
+ break;
+ case LocaleItem::MEASUREMENT_SYSTEM :
+ aLocaleItem[nItem] = aLocaleDataItem.measurementSystem;
+ break;
+ case LocaleItem::TIME_AM :
+ aLocaleItem[nItem] = aLocaleDataItem.timeAM;
+ break;
+ case LocaleItem::TIME_PM :
+ aLocaleItem[nItem] = aLocaleDataItem.timePM;
+ break;
+ case LocaleItem::LONG_DATE_DAY_OF_WEEK_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.LongDateDayOfWeekSeparator;
+ break;
+ case LocaleItem::LONG_DATE_DAY_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.LongDateDaySeparator;
+ break;
+ case LocaleItem::LONG_DATE_MONTH_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.LongDateMonthSeparator;
+ break;
+ case LocaleItem::LONG_DATE_YEAR_SEPARATOR :
+ aLocaleItem[nItem] = aLocaleDataItem.LongDateYearSeparator;
+ break;
+ case LocaleItem::DECIMAL_SEPARATOR_ALTERNATIVE :
+ aLocaleItem[nItem] = aLocaleDataItem.decimalSeparatorAlternative;
+ break;
+ default:
+ SAL_WARN( "unotools.i18n", "getOneLocaleItemImpl: which one?" );
+ }
+}
+
+void LocaleDataWrapper::getOneReservedWordImpl( sal_Int16 nWord )
+{
+ if ( !bReservedWordValid )
+ {
+ aReservedWordSeq = getReservedWord();
+ bReservedWordValid = true;
+ }
+ DBG_ASSERT( nWord < aReservedWordSeq.getLength(), "getOneReservedWordImpl: which one?" );
+ if ( nWord < aReservedWordSeq.getLength() )
+ aReservedWord[nWord] = aReservedWordSeq[nWord];
+}
+
+const OUString& LocaleDataWrapper::getOneReservedWord( sal_Int16 nWord ) const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if ( nWord < 0 || nWord >= reservedWords::COUNT )
+ {
+ SAL_WARN( "unotools.i18n", "getOneReservedWord: bounds" );
+ nWord = reservedWords::FALSE_WORD;
+ }
+ if (aReservedWord[nWord].isEmpty())
+ { // no cached content
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getOneReservedWordImpl( nWord );
+ }
+ return aReservedWord[nWord];
+}
+
+MeasurementSystem LocaleDataWrapper::mapMeasurementStringToEnum( const OUString& rMS ) const
+{
+//! TODO: could be cached too
+ if ( rMS.equalsIgnoreAsciiCase( "metric" ) )
+ return MeasurementSystem::Metric;
+//! TODO: other measurement systems? => extend enum MeasurementSystem
+ return MeasurementSystem::US;
+}
+
+void LocaleDataWrapper::getSecondaryCalendarImpl()
+{
+ if (!xSecondaryCalendar && !bSecondaryCalendarValid)
+ {
+ Sequence< Calendar2 > xCals = getAllCalendars();
+ if (xCals.getLength() > 1)
+ {
+ auto pCal = std::find_if(xCals.begin(), xCals.end(),
+ [](const Calendar2& rCal) { return !rCal.Default; });
+ if (pCal != xCals.end())
+ xSecondaryCalendar = std::make_shared<Calendar2>( *pCal);
+ }
+ bSecondaryCalendarValid = true;
+ }
+}
+
+bool LocaleDataWrapper::doesSecondaryCalendarUseEC( const OUString& rName ) const
+{
+ if (rName.isEmpty())
+ return false;
+
+ // 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")
+ return false;
+
+ ::utl::ReadWriteGuard aGuard( aMutex );
+
+ if (!bSecondaryCalendarValid)
+ { // no cached content
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getSecondaryCalendarImpl();
+ }
+ if (!xSecondaryCalendar)
+ return false;
+ if (!xSecondaryCalendar->Name.equalsIgnoreAsciiCase( rName))
+ return false;
+
+ return true;
+}
+
+void LocaleDataWrapper::getDefaultCalendarImpl()
+{
+ if (!xDefaultCalendar)
+ {
+ Sequence< Calendar2 > xCals = getAllCalendars();
+ auto pCal = xCals.begin();
+ if (xCals.getLength() > 1)
+ {
+ pCal = std::find_if(xCals.begin(), xCals.end(),
+ [](const Calendar2& rCal) { return rCal.Default; });
+ if (pCal == xCals.end())
+ pCal = xCals.begin();
+ }
+ xDefaultCalendar = std::make_shared<Calendar2>( *pCal);
+ }
+}
+
+const std::shared_ptr< css::i18n::Calendar2 >& LocaleDataWrapper::getDefaultCalendar() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if (!xDefaultCalendar)
+ { // no cached content
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getDefaultCalendarImpl();
+ }
+ return xDefaultCalendar;
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > const & LocaleDataWrapper::getDefaultCalendarDays() const
+{
+ return getDefaultCalendar()->Days;
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > const & LocaleDataWrapper::getDefaultCalendarMonths() const
+{
+ return getDefaultCalendar()->Months;
+}
+
+// --- currencies -----------------------------------------------------
+
+const OUString& LocaleDataWrapper::getCurrSymbol() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if (aCurrSymbol.isEmpty())
+ {
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getCurrSymbolsImpl();
+ }
+ return aCurrSymbol;
+}
+
+const OUString& LocaleDataWrapper::getCurrBankSymbol() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if (aCurrBankSymbol.isEmpty())
+ {
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getCurrSymbolsImpl();
+ }
+ return aCurrBankSymbol;
+}
+
+sal_uInt16 LocaleDataWrapper::getCurrPositiveFormat() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if ( nCurrPositiveFormat == nCurrFormatInvalid )
+ {
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getCurrFormatsImpl();
+ }
+ return nCurrPositiveFormat;
+}
+
+sal_uInt16 LocaleDataWrapper::getCurrNegativeFormat() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if ( nCurrNegativeFormat == nCurrFormatInvalid )
+ {
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getCurrFormatsImpl();
+ }
+ return nCurrNegativeFormat;
+}
+
+sal_uInt16 LocaleDataWrapper::getCurrDigits() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if ( nCurrDigits == nCurrFormatInvalid )
+ {
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getCurrSymbolsImpl();
+ }
+ return nCurrDigits;
+}
+
+void LocaleDataWrapper::getCurrSymbolsImpl()
+{
+ Sequence< Currency2 > aCurrSeq = getAllCurrencies();
+ if ( !aCurrSeq.hasElements() )
+ {
+ if (areChecksEnabled())
+ outputCheckMessage(OUString("LocaleDataWrapper::getCurrSymbolsImpl: no currency at all, using ShellsAndPebbles"));
+ aCurrSymbol = "ShellsAndPebbles";
+ aCurrBankSymbol = aCurrSymbol;
+ nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault;
+ nCurrDigits = 2;
+ return;
+ }
+ auto pCurr = std::find_if(aCurrSeq.begin(), aCurrSeq.end(),
+ [](const Currency2& rCurr) { return rCurr.Default; });
+ if ( pCurr == aCurrSeq.end() )
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( "LocaleDataWrapper::getCurrSymbolsImpl: no default currency" ) );
+ }
+ pCurr = aCurrSeq.begin();
+ }
+ aCurrSymbol = pCurr->Symbol;
+ aCurrBankSymbol = pCurr->BankSymbol;
+ nCurrDigits = pCurr->DecimalPlaces;
+}
+
+void LocaleDataWrapper::scanCurrFormatImpl( const OUString& rCode,
+ sal_Int32 nStart, sal_Int32& nSign, sal_Int32& nPar,
+ sal_Int32& nNum, sal_Int32& nBlank, sal_Int32& nSym ) const
+{
+ nSign = nPar = nNum = nBlank = nSym = -1;
+ const sal_Unicode* const pStr = rCode.getStr();
+ const sal_Unicode* const pStop = pStr + rCode.getLength();
+ const sal_Unicode* p = pStr + nStart;
+ int nInSection = 0;
+ bool bQuote = false;
+ while ( p < pStop )
+ {
+ if ( bQuote )
+ {
+ if ( *p == '"' && *(p-1) != '\\' )
+ bQuote = false;
+ }
+ else
+ {
+ switch ( *p )
+ {
+ case '"' :
+ if ( pStr == p || *(p-1) != '\\' )
+ bQuote = true;
+ break;
+ case '-' :
+ if (!nInSection && nSign == -1)
+ nSign = p - pStr;
+ break;
+ case '(' :
+ if (!nInSection && nPar == -1)
+ nPar = p - pStr;
+ break;
+ case '0' :
+ case '#' :
+ if (!nInSection && nNum == -1)
+ nNum = p - pStr;
+ break;
+ case '[' :
+ nInSection++;
+ break;
+ case ']' :
+ if ( nInSection )
+ {
+ nInSection--;
+ if (!nInSection && nBlank == -1
+ && nSym != -1 && p < pStop-1 && *(p+1) == ' ' )
+ nBlank = p - pStr + 1;
+ }
+ break;
+ case '$' :
+ if (nSym == -1 && nInSection && *(p-1) == '[')
+ {
+ nSym = p - pStr + 1;
+ if (nNum != -1 && *(p-2) == ' ')
+ nBlank = p - pStr - 2;
+ }
+ break;
+ case ';' :
+ if ( !nInSection )
+ p = pStop;
+ break;
+ default:
+ if (!nInSection && nSym == -1 && rCode.match(aCurrSymbol, static_cast<sal_Int32>(p - pStr)))
+ { // currency symbol not surrounded by [$...]
+ nSym = p - pStr;
+ if (nBlank == -1 && pStr < p && *(p-1) == ' ')
+ nBlank = p - pStr - 1;
+ p += aCurrSymbol.getLength() - 1;
+ if (nBlank == -1 && p < pStop-2 && *(p+2) == ' ')
+ nBlank = p - pStr + 2;
+ }
+ }
+ }
+ p++;
+ }
+}
+
+void LocaleDataWrapper::getCurrFormatsImpl()
+{
+ css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
+ uno::Sequence< NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( KNumberFormatUsage::CURRENCY, getMyLocale() );
+ sal_Int32 nCnt = aFormatSeq.getLength();
+ if ( !nCnt )
+ { // bad luck
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( "LocaleDataWrapper::getCurrFormatsImpl: no currency formats" ) );
+ }
+ nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault;
+ return;
+ }
+ // find a negative code (medium preferred) and a default (medium preferred) (not necessarily the same)
+ NumberFormatCode const * const pFormatArr = aFormatSeq.getArray();
+ sal_Int32 nElem, nDef, nNeg, nMedium;
+ nDef = nNeg = nMedium = -1;
+ for ( nElem = 0; nElem < nCnt; nElem++ )
+ {
+ if ( pFormatArr[nElem].Type == KNumberFormatType::MEDIUM )
+ {
+ if ( pFormatArr[nElem].Default )
+ {
+ nDef = nElem;
+ nMedium = nElem;
+ if ( pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
+ nNeg = nElem;
+ }
+ else
+ {
+ if ( (nNeg == -1 || nMedium == -1) && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
+ nNeg = nElem;
+ if ( nMedium == -1 )
+ nMedium = nElem;
+ }
+ }
+ else
+ {
+ if ( nDef == -1 && pFormatArr[nElem].Default )
+ nDef = nElem;
+ if ( nNeg == -1 && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
+ nNeg = nElem;
+ }
+ }
+
+ // make sure it's loaded
+ getCurrSymbol();
+
+ sal_Int32 nSign, nPar, nNum, nBlank, nSym;
+
+ // positive format
+ nElem = (nDef >= 0 ? nDef : (nNeg >= 0 ? nNeg : 0));
+ scanCurrFormatImpl( pFormatArr[nElem].Code, 0, nSign, nPar, nNum, nBlank, nSym );
+ if (areChecksEnabled() && (nNum == -1 || nSym == -1))
+ {
+ outputCheckMessage( appendLocaleInfo( "LocaleDataWrapper::getCurrFormatsImpl: CurrPositiveFormat?" ) );
+ }
+ if (nBlank == -1)
+ {
+ if ( nSym < nNum )
+ nCurrPositiveFormat = 0; // $1
+ else
+ nCurrPositiveFormat = 1; // 1$
+ }
+ else
+ {
+ if ( nSym < nNum )
+ nCurrPositiveFormat = 2; // $ 1
+ else
+ nCurrPositiveFormat = 3; // 1 $
+ }
+
+ // negative format
+ if ( nNeg < 0 )
+ nCurrNegativeFormat = nCurrFormatDefault;
+ else
+ {
+ const OUString& rCode = pFormatArr[nNeg].Code;
+ sal_Int32 nDelim = rCode.indexOf(';');
+ scanCurrFormatImpl( rCode, nDelim+1, nSign, nPar, nNum, nBlank, nSym );
+ if (areChecksEnabled() && (nNum == -1 || nSym == -1 || (nPar == -1 && nSign == -1)))
+ {
+ outputCheckMessage( appendLocaleInfo( "LocaleDataWrapper::getCurrFormatsImpl: CurrNegativeFormat?" ) );
+ }
+ // NOTE: one of nPar or nSign are allowed to be -1
+ if (nBlank == -1)
+ {
+ if ( nSym < nNum )
+ {
+ if ( -1 < nPar && nPar < nSym )
+ nCurrNegativeFormat = 0; // ($1)
+ else if ( -1 < nSign && nSign < nSym )
+ nCurrNegativeFormat = 1; // -$1
+ else if ( nNum < nSign )
+ nCurrNegativeFormat = 3; // $1-
+ else
+ nCurrNegativeFormat = 2; // $-1
+ }
+ else
+ {
+ if ( -1 < nPar && nPar < nNum )
+ nCurrNegativeFormat = 4; // (1$)
+ else if ( -1 < nSign && nSign < nNum )
+ nCurrNegativeFormat = 5; // -1$
+ else if ( nSym < nSign )
+ nCurrNegativeFormat = 7; // 1$-
+ else
+ nCurrNegativeFormat = 6; // 1-$
+ }
+ }
+ else
+ {
+ if ( nSym < nNum )
+ {
+ if ( -1 < nPar && nPar < nSym )
+ nCurrNegativeFormat = 14; // ($ 1)
+ else if ( -1 < nSign && nSign < nSym )
+ nCurrNegativeFormat = 9; // -$ 1
+ else if ( nNum < nSign )
+ nCurrNegativeFormat = 12; // $ 1-
+ else
+ nCurrNegativeFormat = 11; // $ -1
+ }
+ else
+ {
+ if ( -1 < nPar && nPar < nNum )
+ nCurrNegativeFormat = 15; // (1 $)
+ else if ( -1 < nSign && nSign < nNum )
+ nCurrNegativeFormat = 8; // -1 $
+ else if ( nSym < nSign )
+ nCurrNegativeFormat = 10; // 1 $-
+ else
+ nCurrNegativeFormat = 13; // 1- $
+ }
+ }
+ }
+}
+
+// --- date -----------------------------------------------------------
+
+DateOrder LocaleDataWrapper::getDateOrder() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if ( nDateOrder == DateOrder::Invalid )
+ {
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getDateOrdersImpl();
+ }
+ return nDateOrder;
+}
+
+DateOrder LocaleDataWrapper::getLongDateOrder() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if ( nLongDateOrder == DateOrder::Invalid )
+ {
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getDateOrdersImpl();
+ }
+ return nLongDateOrder;
+}
+
+DateOrder LocaleDataWrapper::scanDateOrderImpl( const OUString& 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
+ sal_Int32 nDay = rCode.indexOf('D');
+ sal_Int32 nMonth = rCode.indexOf('M');
+ sal_Int32 nYear = rCode.indexOf('Y');
+ if (nDay == -1 || nMonth == -1 || nYear == -1)
+ { // This algorithm assumes that all three parts (DMY) are present
+ if (nMonth == -1)
+ { // only Finnish has something else than 'M' for month
+ nMonth = rCode.indexOf('K');
+ if (nMonth != -1)
+ {
+ nDay = rCode.indexOf('P');
+ nYear = rCode.indexOf('V');
+ }
+ }
+ else if (nDay == -1)
+ { // We have a month 'M' if we reach this branch.
+ // Possible languages containing 'M' but no 'D':
+ // German, French, Italian
+ nDay = rCode.indexOf('T'); // German
+ if (nDay != -1)
+ nYear = rCode.indexOf('J');
+ else
+ {
+ nYear = rCode.indexOf('A'); // French, Italian
+ if (nYear != -1)
+ {
+ nDay = rCode.indexOf('J'); // French
+ if (nDay == -1)
+ nDay = rCode.indexOf('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.indexOf('A'); // Spanish
+ if (nYear == -1)
+ nYear = rCode.indexOf('J'); // Dutch
+ }
+ if (nDay == -1 || nMonth == -1 || nYear == -1)
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( "LocaleDataWrapper::scanDateOrder: not all DMY present" ) );
+ }
+ if (nDay == -1)
+ nDay = rCode.getLength();
+ if (nMonth == -1)
+ nMonth = rCode.getLength();
+ if (nYear == -1)
+ nYear = rCode.getLength();
+ }
+ }
+ // compare with <= because each position may equal rCode.getLength()
+ if ( nDay <= nMonth && nMonth <= nYear )
+ return DateOrder::DMY; // also if every position equals rCode.getLength()
+ else if ( nMonth <= nDay && nDay <= nYear )
+ return DateOrder::MDY;
+ else if ( nYear <= nMonth && nMonth <= nDay )
+ return DateOrder::YMD;
+ else
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( "LocaleDataWrapper::scanDateOrder: no magic applicable" ) );
+ }
+ return DateOrder::DMY;
+ }
+}
+
+void LocaleDataWrapper::getDateOrdersImpl()
+{
+ css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
+ uno::Sequence< NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( KNumberFormatUsage::DATE, getMyLocale() );
+ sal_Int32 nCnt = aFormatSeq.getLength();
+ if ( !nCnt )
+ { // bad luck
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( "LocaleDataWrapper::getDateOrdersImpl: no date formats" ) );
+ }
+ nDateOrder = nLongDateOrder = DateOrder::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;
+ }
+ else if ( nMedium == -1 )
+ nMedium = nElem;
+ }
+ break;
+ case KNumberFormatType::LONG :
+ {
+ if ( pFormatArr[nElem].Default )
+ nLong = nElem;
+ else if ( nLong == -1 )
+ nLong = nElem;
+ }
+ break;
+ }
+ }
+ if ( nEdit == -1 )
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( "LocaleDataWrapper::getDateOrdersImpl: no edit" ) );
+ }
+ if ( nDef == -1 )
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( "LocaleDataWrapper::getDateOrdersImpl: no default" ) );
+ }
+ if ( nMedium != -1 )
+ nDef = nMedium;
+ else if ( nLong != -1 )
+ nDef = nLong;
+ else
+ nDef = 0;
+ }
+ nEdit = nDef;
+ }
+ DateOrder nDF = scanDateOrderImpl( pFormatArr[nEdit].Code );
+ if ( pFormatArr[nEdit].Type == KNumberFormatType::LONG )
+ { // normally this is not the case
+ nLongDateOrder = nDateOrder = nDF;
+ }
+ else
+ {
+ nDateOrder = nDF;
+ if ( nLong == -1 )
+ nLongDateOrder = nDF;
+ else
+ nLongDateOrder = scanDateOrderImpl( pFormatArr[nLong].Code );
+ }
+}
+
+// --- digit grouping -------------------------------------------------
+
+void LocaleDataWrapper::getDigitGroupingImpl()
+{
+ /* 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.realloc(3); // room for {3,2,0}
+ aGrouping[0] = 0; // invalidate
+ }
+ if (!aGrouping[0])
+ {
+ i18n::LanguageCountryInfo aLCInfo( getLanguageCountryInfo());
+ if (aLCInfo.Country.equalsIgnoreAsciiCase("IN") || // India
+ aLCInfo.Country.equalsIgnoreAsciiCase("BT") ) // Bhutan
+ {
+ aGrouping[0] = 3;
+ aGrouping[1] = 2;
+ aGrouping[2] = 0;
+ }
+ else
+ {
+ aGrouping[0] = 3;
+ aGrouping[1] = 0;
+ }
+ }
+}
+
+css::uno::Sequence< sal_Int32 > LocaleDataWrapper::getDigitGrouping() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+ if (!aGrouping.hasElements() || aGrouping[0] == 0)
+ { // no cached content
+ aGuard.changeReadToWrite();
+ const_cast<LocaleDataWrapper*>(this)->getDigitGroupingImpl();
+ }
+ return aGrouping;
+}
+
+// --- simple number formatting helpers -------------------------------
+
+// The ImplAdd... methods are taken from class International and modified to
+// suit the needs.
+
+static void ImplAddUNum( OUStringBuffer& rBuf, sal_uInt64 nNumber )
+{
+ // fill temp buffer with digits
+ sal_Unicode aTempBuf[64];
+ sal_Unicode* pTempBuf = aTempBuf;
+ do
+ {
+ *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
+ pTempBuf++;
+ nNumber /= 10;
+ }
+ while ( nNumber );
+
+ // copy temp buffer to buffer passed
+ do
+ {
+ pTempBuf--;
+ rBuf.append(*pTempBuf);
+ }
+ while ( pTempBuf != aTempBuf );
+}
+
+static void ImplAddUNum( OUStringBuffer& rBuf, sal_uInt64 nNumber, int nMinLen )
+{
+ // fill temp buffer with digits
+ sal_Unicode aTempBuf[64];
+ sal_Unicode* pTempBuf = aTempBuf;
+ do
+ {
+ *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
+ pTempBuf++;
+ nNumber /= 10;
+ nMinLen--;
+ }
+ while ( nNumber );
+
+ // fill with zeros up to the minimal length
+ while ( nMinLen > 0 )
+ {
+ rBuf.append('0');
+ nMinLen--;
+ }
+
+ // copy temp buffer to real buffer
+ do
+ {
+ pTempBuf--;
+ rBuf.append(*pTempBuf);
+ }
+ while ( pTempBuf != aTempBuf );
+}
+
+static void ImplAddNum( OUStringBuffer& rBuf, sal_Int64 nNumber, int nMinLen )
+{
+ if (nNumber < 0)
+ {
+ rBuf.append('-');
+ nNumber = -nNumber;
+ }
+ return ImplAddUNum( rBuf, nNumber, nMinLen);
+}
+
+static void ImplAdd2UNum( OUStringBuffer& rBuf, sal_uInt16 nNumber, bool bLeading )
+{
+ DBG_ASSERT( nNumber < 100, "ImplAdd2UNum() - Number >= 100" );
+
+ if ( nNumber < 10 )
+ {
+ if ( bLeading )
+ {
+ rBuf.append('0');
+ }
+ rBuf.append(static_cast<char>(nNumber + '0'));
+ }
+ else
+ {
+ sal_uInt16 nTemp = nNumber % 10;
+ nNumber /= 10;
+ rBuf.append(static_cast<char>(nNumber + '0'));
+ rBuf.append(static_cast<char>(nTemp + '0'));
+ }
+}
+
+static void ImplAdd9UNum( OUStringBuffer& rBuf, sal_uInt32 nNumber )
+{
+ DBG_ASSERT( nNumber < 1000000000, "ImplAdd9UNum() - Number >= 1000000000" );
+
+ std::ostringstream ostr;
+ ostr.fill('0');
+ ostr.width(9);
+ ostr << nNumber;
+ std::string aStr = ostr.str();
+ rBuf.appendAscii(aStr.c_str(), aStr.size());
+}
+
+void LocaleDataWrapper::ImplAddFormatNum( OUStringBuffer& rBuf,
+ sal_Int64 nNumber, sal_uInt16 nDecimals, bool bUseThousandSep,
+ bool bTrailingZeros ) const
+{
+ OUStringBuffer aNumBuf(64);
+ sal_uInt16 nNumLen;
+
+ // negative number
+ if ( nNumber < 0 )
+ {
+ nNumber *= -1;
+ rBuf.append('-');
+ }
+
+ // convert number
+ ImplAddUNum( aNumBuf, static_cast<sal_uInt64>(nNumber) );
+ nNumLen = static_cast<sal_uInt16>(aNumBuf.getLength());
+
+ if ( nNumLen <= nDecimals )
+ {
+ // strip .0 in decimals?
+ if ( !nNumber && !bTrailingZeros )
+ {
+ rBuf.append('0');
+ }
+ else
+ {
+ // LeadingZero, insert 0
+ if ( isNumLeadingZero() )
+ {
+ rBuf.append('0');
+ }
+
+ // append decimal separator
+ rBuf.append( getNumDecimalSep() );
+
+ // fill with zeros
+ sal_uInt16 i = 0;
+ while ( i < (nDecimals-nNumLen) )
+ {
+ rBuf.append('0');
+ i++;
+ }
+
+ // append decimals
+ rBuf.append(aNumBuf);
+ }
+ }
+ else
+ {
+ const OUString& rThoSep = getNumThousandSep();
+
+ // copy number to buffer (excluding decimals)
+ sal_uInt16 nNumLen2 = nNumLen-nDecimals;
+ uno::Sequence< sal_Bool > aGroupPos;
+ if (bUseThousandSep)
+ aGroupPos = utl::DigitGroupingIterator::createForwardSequence(
+ nNumLen2, getDigitGrouping());
+ sal_uInt16 i = 0;
+ for (; i < nNumLen2; ++i )
+ {
+ rBuf.append(aNumBuf[i]);
+
+ // add thousand separator?
+ if ( bUseThousandSep && aGroupPos[i] )
+ rBuf.append( rThoSep );
+ }
+
+ // append decimals
+ if ( nDecimals )
+ {
+ rBuf.append( getNumDecimalSep() );
+
+ bool bNullEnd = true;
+ while ( i < nNumLen )
+ {
+ if ( aNumBuf[i] != '0' )
+ bNullEnd = false;
+
+ rBuf.append(aNumBuf[i]);
+ i++;
+ }
+
+ // strip .0 in decimals?
+ if ( bNullEnd && !bTrailingZeros )
+ rBuf.setLength( rBuf.getLength() - (nDecimals + 1) );
+ }
+ }
+}
+
+// --- simple date and time formatting --------------------------------
+
+OUString LocaleDataWrapper::getDate( const Date& rDate ) const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex, ReadWriteGuardMode::BlockCritical );
+//!TODO: leading zeros et al
+ OUStringBuffer aBuf(128);
+ sal_uInt16 nDay = rDate.GetDay();
+ sal_uInt16 nMonth = rDate.GetMonth();
+ sal_Int16 nYear = rDate.GetYear();
+ sal_uInt16 nYearLen;
+
+ if ( (true) /* IsDateCentury() */ )
+ nYearLen = 4;
+ else
+ {
+ nYearLen = 2;
+ nYear %= 100;
+ }
+
+ switch ( getDateOrder() )
+ {
+ case DateOrder::DMY :
+ ImplAdd2UNum( aBuf, nDay, true /* IsDateDayLeadingZero() */ );
+ aBuf.append( getDateSep() );
+ ImplAdd2UNum( aBuf, nMonth, true /* IsDateMonthLeadingZero() */ );
+ aBuf.append( getDateSep() );
+ ImplAddNum( aBuf, nYear, nYearLen );
+ break;
+ case DateOrder::MDY :
+ ImplAdd2UNum( aBuf, nMonth, true /* IsDateMonthLeadingZero() */ );
+ aBuf.append( getDateSep() );
+ ImplAdd2UNum( aBuf, nDay, true /* IsDateDayLeadingZero() */ );
+ aBuf.append( getDateSep() );
+ ImplAddNum( aBuf, nYear, nYearLen );
+ break;
+ default:
+ ImplAddNum( aBuf, nYear, nYearLen );
+ aBuf.append( getDateSep() );
+ ImplAdd2UNum( aBuf, nMonth, true /* IsDateMonthLeadingZero() */ );
+ aBuf.append( getDateSep() );
+ ImplAdd2UNum( aBuf, nDay, true /* IsDateDayLeadingZero() */ );
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString LocaleDataWrapper::getTime( const tools::Time& rTime, bool bSec, bool b100Sec ) const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex, ReadWriteGuardMode::BlockCritical );
+//!TODO: leading zeros et al
+ OUStringBuffer aBuf(128);
+ sal_uInt16 nHour = rTime.GetHour();
+
+ nHour %= 24;
+
+ ImplAdd2UNum( aBuf, nHour, true /* IsTimeLeadingZero() */ );
+ aBuf.append( getTimeSep() );
+ ImplAdd2UNum( aBuf, rTime.GetMin(), true );
+ if ( bSec )
+ {
+ aBuf.append( getTimeSep() );
+ ImplAdd2UNum( aBuf, rTime.GetSec(), true );
+
+ if ( b100Sec )
+ {
+ aBuf.append( getTime100SecSep() );
+ ImplAdd9UNum( aBuf, rTime.GetNanoSec() );
+ }
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString LocaleDataWrapper::getLongDate( const Date& rDate, CalendarWrapper& rCal,
+ bool bTwoDigitYear ) const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex, ReadWriteGuardMode::BlockCritical );
+ OUStringBuffer aBuf(20);
+ OUStringBuffer aStr(120); // complete guess
+ sal_Int16 nVal;
+ rCal.setGregorianDateTime( rDate );
+ // day of week
+ nVal = rCal.getValue( CalendarFieldIndex::DAY_OF_WEEK );
+ aStr.append(rCal.getDisplayName( CalendarDisplayIndex::DAY, nVal, 1 ));
+ aStr.append(getLongDateDayOfWeekSep());
+ // day of month
+ nVal = rCal.getValue( CalendarFieldIndex::DAY_OF_MONTH );
+ ImplAdd2UNum( aBuf, nVal, false/*bDayOfMonthWithLeadingZero*/ );
+ OUString aDay = aBuf.makeStringAndClear();
+ // month of year
+ nVal = rCal.getValue( CalendarFieldIndex::MONTH );
+ OUString aMonth( rCal.getDisplayName( CalendarDisplayIndex::MONTH, nVal, 1 ) );
+ // year
+ nVal = rCal.getValue( CalendarFieldIndex::YEAR );
+ if ( bTwoDigitYear )
+ ImplAddUNum( aBuf, nVal % 100, 2 );
+ else
+ ImplAddUNum( aBuf, nVal );
+ OUString aYear = aBuf.makeStringAndClear();
+ // concatenate
+ switch ( getLongDateOrder() )
+ {
+ case DateOrder::DMY :
+ aStr.append(aDay).append(getLongDateDaySep()).append(aMonth).append(getLongDateMonthSep()).append(aYear);
+ break;
+ case DateOrder::MDY :
+ aStr.append(aMonth).append(getLongDateMonthSep()).append(aDay).append(getLongDateDaySep()).append(aYear);
+ break;
+ default: // YMD
+ aStr.append(aYear).append(getLongDateYearSep()).append(aMonth).append(getLongDateMonthSep()).append(aDay);
+ }
+ return aStr.makeStringAndClear();
+}
+
+OUString LocaleDataWrapper::getDuration( const tools::Time& rTime, bool bSec, bool b100Sec ) const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex, ReadWriteGuardMode::BlockCritical );
+ OUStringBuffer aBuf(128);
+
+ if ( rTime < tools::Time( 0 ) )
+ aBuf.append(' ' );
+
+ if ( (true) /* IsTimeLeadingZero() */ )
+ ImplAddUNum( aBuf, rTime.GetHour(), 2 );
+ else
+ ImplAddUNum( aBuf, rTime.GetHour() );
+ aBuf.append( getTimeSep() );
+ ImplAdd2UNum( aBuf, rTime.GetMin(), true );
+ if ( bSec )
+ {
+ aBuf.append( getTimeSep() );
+ ImplAdd2UNum( aBuf, rTime.GetSec(), true );
+
+ if ( b100Sec )
+ {
+ aBuf.append( getTime100SecSep() );
+ ImplAdd9UNum( aBuf, rTime.GetNanoSec() );
+ }
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+// --- simple number formatting ---------------------------------------
+
+static size_t ImplGetNumberStringLengthGuess( const LocaleDataWrapper& rLoc, sal_uInt16 nDecimals )
+{
+ // approximately 3.2 bits per digit
+ const size_t nDig = ((sizeof(sal_Int64) * 8) / 3) + 1;
+ // digits, separators (pessimized for insane "every digit may be grouped"), leading zero, sign
+ size_t nGuess = ((nDecimals < nDig) ?
+ (((nDig - nDecimals) * rLoc.getNumThousandSep().getLength()) + nDig) :
+ nDecimals) + rLoc.getNumDecimalSep().getLength() + 3;
+ return nGuess;
+}
+
+OUString LocaleDataWrapper::getNum( sal_Int64 nNumber, sal_uInt16 nDecimals,
+ bool bUseThousandSep, bool bTrailingZeros ) const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex, ReadWriteGuardMode::BlockCritical );
+ // check if digits and separators will fit into fixed buffer or allocate
+ size_t nGuess = ImplGetNumberStringLengthGuess( *this, nDecimals );
+ OUStringBuffer aBuf(int(nGuess + 16));
+
+ ImplAddFormatNum( aBuf, nNumber, nDecimals,
+ bUseThousandSep, bTrailingZeros );
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString LocaleDataWrapper::getCurr( sal_Int64 nNumber, sal_uInt16 nDecimals,
+ const OUString& rCurrencySymbol, bool bUseThousandSep ) const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex, ReadWriteGuardMode::BlockCritical );
+ sal_Unicode cZeroChar = getCurrZeroChar();
+
+ // check if digits and separators will fit into fixed buffer or allocate
+ size_t nGuess = ImplGetNumberStringLengthGuess( *this, nDecimals );
+ OUStringBuffer aNumBuf(int(nGuess + 16));
+ OUStringBuffer aBuf(int(rCurrencySymbol.getLength() + nGuess + 20 ));
+
+ bool bNeg;
+ if ( nNumber < 0 )
+ {
+ bNeg = true;
+ nNumber *= -1;
+ }
+ else
+ bNeg = false;
+
+ // convert number
+ ImplAddFormatNum( aNumBuf, nNumber, nDecimals,
+ bUseThousandSep, true );
+ const sal_Int32 nNumLen = aNumBuf.getLength();
+
+ // replace zeros with zero character
+ if ( (cZeroChar != '0') && nDecimals /* && IsNumTrailingZeros() */ )
+ {
+ sal_uInt16 i;
+ bool bZero = true;
+
+ sal_uInt16 nNumBufIndex = nNumLen-nDecimals;
+ i = 0;
+ do
+ {
+ if ( aNumBuf[nNumBufIndex] != '0' )
+ {
+ bZero = false;
+ break;
+ }
+
+ nNumBufIndex++;
+ i++;
+ }
+ while ( i < nDecimals );
+
+ if ( bZero )
+ {
+ nNumBufIndex = nNumLen-nDecimals;
+ i = 0;
+ do
+ {
+ aNumBuf[nNumBufIndex] = cZeroChar;
+ nNumBufIndex++;
+ i++;
+ }
+ while ( i < nDecimals );
+ }
+ }
+
+ if ( !bNeg )
+ {
+ switch( getCurrPositiveFormat() )
+ {
+ case 0:
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( aNumBuf );
+ break;
+ case 1:
+ aBuf.append( aNumBuf );
+ aBuf.append( rCurrencySymbol );
+ break;
+ case 2:
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( ' ' );
+ aBuf.append( aNumBuf );
+ break;
+ case 3:
+ aBuf.append( aNumBuf );
+ aBuf.append( ' ' );
+ aBuf.append( rCurrencySymbol );
+ break;
+ }
+ }
+ else
+ {
+ switch( getCurrNegativeFormat() )
+ {
+ case 0:
+ aBuf.append( '(' );
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( aNumBuf );
+ aBuf.append( ')' );
+ break;
+ case 1:
+ aBuf.append( '-' );
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( aNumBuf );
+ break;
+ case 2:
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( '-' );
+ aBuf.append( aNumBuf );
+ break;
+ case 3:
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( aNumBuf );
+ aBuf.append( '-' );
+ break;
+ case 4:
+ aBuf.append( '(' );
+ aBuf.append( aNumBuf );
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( ')' );
+ break;
+ case 5:
+ aBuf.append( '-' );
+ aBuf.append( aNumBuf );
+ aBuf.append( rCurrencySymbol );
+ break;
+ case 6:
+ aBuf.append( aNumBuf );
+ aBuf.append( '-' );
+ aBuf.append( rCurrencySymbol );
+ break;
+ case 7:
+ aBuf.append( aNumBuf );
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( '-' );
+ break;
+ case 8:
+ aBuf.append( '-' );
+ aBuf.append( aNumBuf );
+ aBuf.append( ' ' );
+ aBuf.append( rCurrencySymbol );
+ break;
+ case 9:
+ aBuf.append( '-' );
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( ' ' );
+ aBuf.append( aNumBuf );
+ break;
+ case 10:
+ aBuf.append( aNumBuf );
+ aBuf.append( ' ' );
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( '-' );
+ break;
+ case 11:
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( ' ' );
+ aBuf.append( '-' );
+ aBuf.append( aNumBuf );
+ break;
+ case 12:
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( ' ' );
+ aBuf.append( aNumBuf );
+ aBuf.append( '-' );
+ break;
+ case 13:
+ aBuf.append( aNumBuf );
+ aBuf.append( '-' );
+ aBuf.append( ' ' );
+ aBuf.append( rCurrencySymbol );
+ break;
+ case 14:
+ aBuf.append( '(' );
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( ' ' );
+ aBuf.append( aNumBuf );
+ aBuf.append( ')' );
+ break;
+ case 15:
+ aBuf.append( '(' );
+ aBuf.append( aNumBuf );
+ aBuf.append( ' ' );
+ aBuf.append( rCurrencySymbol );
+ aBuf.append( ')' );
+ break;
+ }
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+// --- number parsing -------------------------------------------------
+
+double LocaleDataWrapper::stringToDouble( const OUString& rString, bool bUseGroupSep,
+ rtl_math_ConversionStatus* pStatus, sal_Int32* pParseEnd ) const
+{
+ const sal_Unicode cGroupSep = (bUseGroupSep ? getNumThousandSep()[0] : 0);
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ sal_Int32 nParseEnd = 0;
+ double fValue = rtl::math::stringToDouble( rString, getNumDecimalSep()[0], cGroupSep, &eStatus, &nParseEnd);
+ bool bTryAlt = (nParseEnd < rString.getLength() && !getNumDecimalSepAlt().isEmpty() &&
+ rString[nParseEnd] == getNumDecimalSepAlt().toChar());
+ // Try re-parsing with alternative if that was the reason to stop.
+ if (bTryAlt)
+ fValue = rtl::math::stringToDouble( rString, getNumDecimalSepAlt().toChar(), cGroupSep, &eStatus, &nParseEnd);
+ if (pStatus)
+ *pStatus = eStatus;
+ if (pParseEnd)
+ *pParseEnd = nParseEnd;
+ return fValue;
+}
+
+double LocaleDataWrapper::stringToDouble( const sal_Unicode* pBegin, const sal_Unicode* pEnd, bool bUseGroupSep,
+ rtl_math_ConversionStatus* pStatus, const sal_Unicode** ppParseEnd ) const
+{
+ const sal_Unicode cGroupSep = (bUseGroupSep ? getNumThousandSep()[0] : 0);
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ const sal_Unicode* pParseEnd = nullptr;
+ double fValue = rtl_math_uStringToDouble( pBegin, pEnd, getNumDecimalSep()[0], cGroupSep, &eStatus, &pParseEnd);
+ bool bTryAlt = (pParseEnd < pEnd && !getNumDecimalSepAlt().isEmpty() &&
+ *pParseEnd == getNumDecimalSepAlt().toChar());
+ // Try re-parsing with alternative if that was the reason to stop.
+ if (bTryAlt)
+ fValue = rtl_math_uStringToDouble( pBegin, pEnd, getNumDecimalSepAlt().toChar(), cGroupSep, &eStatus, &pParseEnd);
+ if (pStatus)
+ *pStatus = eStatus;
+ if (ppParseEnd)
+ *ppParseEnd = pParseEnd;
+ return fValue;
+}
+
+// --- mixed ----------------------------------------------------------
+
+LanguageTag LocaleDataWrapper::getLoadedLanguageTag() const
+{
+ LanguageCountryInfo aLCInfo = getLanguageCountryInfo();
+ return LanguageTag( lang::Locale( aLCInfo.Language, aLCInfo.Country, aLCInfo.Variant ));
+}
+
+OUString LocaleDataWrapper::appendLocaleInfo(const OUString& rDebugMsg) const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex, ReadWriteGuardMode::BlockCritical );
+ OUStringBuffer aDebugMsg(rDebugMsg);
+ aDebugMsg.append('\n');
+ aDebugMsg.append(maLanguageTag.getBcp47());
+ aDebugMsg.append(" requested\n");
+ LanguageTag aLoaded = getLoadedLanguageTag();
+ aDebugMsg.append(aLoaded.getBcp47());
+ aDebugMsg.append(" loaded");
+ return aDebugMsg.makeStringAndClear();
+}
+
+// static
+void LocaleDataWrapper::outputCheckMessage( const OUString& rMsg )
+{
+ outputCheckMessage(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+// static
+void LocaleDataWrapper::outputCheckMessage( const char* pStr )
+{
+ fprintf( stderr, "\n%s\n", pStr);
+ fflush( stderr);
+ SAL_WARN("unotools.i18n", pStr);
+}
+
+// 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
+ const char* 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();
+ }
+}
+
+// --- XLocaleData3 ----------------------------------------------------------
+
+css::uno::Sequence< css::i18n::Calendar2 > LocaleDataWrapper::getAllCalendars() const
+{
+ try
+ {
+ return xLD->getAllCalendars2( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCalendars" );
+ }
+ return css::uno::Sequence< css::i18n::Calendar2 >(0);
+}
+
+// --- XLocaleData4 ----------------------------------------------------------
+
+css::uno::Sequence< OUString > LocaleDataWrapper::getDateAcceptancePatterns() const
+{
+ ::utl::ReadWriteGuard aGuard( aMutex );
+
+ if (aDateAcceptancePatterns.hasElements())
+ return aDateAcceptancePatterns;
+
+ aGuard.changeReadToWrite();
+
+ try
+ {
+ const_cast<LocaleDataWrapper*>(this)->aDateAcceptancePatterns =
+ xLD->getDateAcceptancePatterns( getMyLocale() );
+ return aDateAcceptancePatterns;
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDateAcceptancePatterns" );
+ }
+ return css::uno::Sequence< OUString >(0);
+}
+
+// --- Override layer --------------------------------------------------------
+
+void LocaleDataWrapper::setDateAcceptancePatterns(
+ const css::uno::Sequence< OUString > & rPatterns )
+{
+ ::utl::ReadWriteGuard aGuard( aMutex, ReadWriteGuardMode::Write );
+
+ if (!aDateAcceptancePatterns.hasElements() || !rPatterns.hasElements())
+ {
+ try
+ {
+ aDateAcceptancePatterns = xLD->getDateAcceptancePatterns( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "setDateAcceptancePatterns" );
+ }
+ if (!rPatterns.hasElements())
+ return; // just a reset
+ if (!aDateAcceptancePatterns.hasElements())
+ {
+ aDateAcceptancePatterns = rPatterns;
+ return;
+ }
+ }
+
+ // Never overwrite the locale's full date pattern! The first.
+ if (aDateAcceptancePatterns[0] == rPatterns[0])
+ aDateAcceptancePatterns = rPatterns; // sane
+ else
+ {
+ // Copy existing full date pattern and append the sequence passed.
+ /* TODO: could check for duplicates and shrink target sequence */
+ Sequence< OUString > aTmp( rPatterns.getLength() + 1 );
+ aTmp[0] = aDateAcceptancePatterns[0];
+ std::copy(rPatterns.begin(), rPatterns.end(), std::next(aTmp.begin()));
+ aDateAcceptancePatterns = aTmp;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/nativenumberwrapper.cxx b/unotools/source/i18n/nativenumberwrapper.cxx
new file mode 100644
index 000000000..26fcbd7fa
--- /dev/null
+++ b/unotools/source/i18n/nativenumberwrapper.cxx
@@ -0,0 +1,110 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+#include <unotools/nativenumberwrapper.hxx>
+#include <com/sun/star/i18n/NativeNumberSupplier2.hpp>
+
+using namespace ::com::sun::star;
+
+NativeNumberWrapper::NativeNumberWrapper(
+ const uno::Reference< uno::XComponentContext > & rxContext
+ )
+{
+ xNNS = i18n::NativeNumberSupplier2::create(rxContext);
+}
+
+NativeNumberWrapper::~NativeNumberWrapper()
+{
+}
+
+OUString
+NativeNumberWrapper::getNativeNumberString(
+ const OUString& rNumberString,
+ const css::lang::Locale& rLocale,
+ sal_Int16 nNativeNumberMode) const
+{
+ try
+ {
+ if ( xNNS.is() )
+ return xNNS->getNativeNumberString(rNumberString, rLocale, nNativeNumberMode);
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "getNativeNumberString: Exception caught!" );
+ }
+ return OUString();
+}
+
+OUString
+NativeNumberWrapper::getNativeNumberStringParams(
+ const OUString& rNumberString,
+ const css::lang::Locale& rLocale,
+ sal_Int16 nNativeNumberMode,
+ const OUString& rNativeNumberParams) const
+{
+ try
+ {
+ if ( xNNS.is() )
+ return xNNS->getNativeNumberStringParams(rNumberString, rLocale, nNativeNumberMode,
+ rNativeNumberParams);
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "getNativeNumberStringParams: Exception caught!" );
+ }
+ return OUString();
+}
+
+i18n::NativeNumberXmlAttributes
+NativeNumberWrapper::convertToXmlAttributes(
+ const css::lang::Locale& rLocale,
+ sal_Int16 nNativeNumberMode ) const
+{
+ try
+ {
+ if ( xNNS.is() )
+ return xNNS->convertToXmlAttributes( rLocale, nNativeNumberMode );
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "convertToXmlAttributes: Exception caught!" );
+ }
+ return i18n::NativeNumberXmlAttributes();
+}
+
+sal_Int16
+NativeNumberWrapper::convertFromXmlAttributes(
+ const i18n::NativeNumberXmlAttributes& rAttr ) const
+{
+ try
+ {
+ if ( xNNS.is() )
+ return xNNS->convertFromXmlAttributes( rAttr );
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "convertFromXmlAttributes: Exception caught!" );
+ }
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/readwritemutexguard.cxx b/unotools/source/i18n/readwritemutexguard.cxx
new file mode 100644
index 000000000..134f06c35
--- /dev/null
+++ b/unotools/source/i18n/readwritemutexguard.cxx
@@ -0,0 +1,109 @@
+/* -*- 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 .
+ */
+
+#include <unotools/readwritemutexguard.hxx>
+#include <tools/debug.hxx>
+
+namespace utl {
+
+ReadWriteGuard::ReadWriteGuard( ReadWriteMutex& rMutexP,
+ ReadWriteGuardMode nRequestMode )
+ : rMutex( rMutexP )
+{
+ // don't do anything until a pending write completed (or another
+ // ReadWriteGuard leaves the ctor phase)
+ ::osl::MutexGuard aGuard( rMutex.maWriteMutex );
+ nMode = nRequestMode;
+ if ( nMode & ReadWriteGuardMode::Write )
+ {
+ rMutex.maWriteMutex.acquire();
+ // wait for any read to complete
+// TODO: set up a waiting thread instead of a loop
+ bool bWait = true;
+ do
+ {
+ rMutex.maMutex.acquire();
+ bWait = (rMutex.nReadCount != 0);
+ if ( nMode & ReadWriteGuardMode::CriticalChange )
+ bWait |= (rMutex.nBlockCriticalCount != 0);
+ rMutex.maMutex.release();
+ } while ( bWait );
+ }
+ else if ( nMode & ReadWriteGuardMode::BlockCritical )
+ {
+ rMutex.maMutex.acquire();
+ ++rMutex.nBlockCriticalCount;
+ rMutex.maMutex.release();
+ }
+ else
+ {
+ rMutex.maMutex.acquire();
+ ++rMutex.nReadCount;
+ rMutex.maMutex.release();
+ }
+}
+
+ReadWriteGuard::~ReadWriteGuard()
+{
+ if ( nMode & ReadWriteGuardMode::Write )
+ rMutex.maWriteMutex.release();
+ else if ( nMode & ReadWriteGuardMode::BlockCritical )
+ {
+ rMutex.maMutex.acquire();
+ --rMutex.nBlockCriticalCount;
+ rMutex.maMutex.release();
+ }
+ else
+ {
+ rMutex.maMutex.acquire();
+ --rMutex.nReadCount;
+ rMutex.maMutex.release();
+ }
+}
+
+void ReadWriteGuard::changeReadToWrite()
+{
+ bool bOk = !(nMode & (ReadWriteGuardMode::Write | ReadWriteGuardMode::BlockCritical));
+ DBG_ASSERT( bOk, "ReadWriteGuard::changeReadToWrite: can't" );
+ if ( bOk )
+ {
+ // MUST release read before acquiring write mutex or dead lock would
+ // occur if there was a write in another thread waiting for this read
+ // to complete.
+ rMutex.maMutex.acquire();
+ --rMutex.nReadCount;
+ rMutex.maMutex.release();
+
+ rMutex.maWriteMutex.acquire();
+ nMode |= ReadWriteGuardMode::Write;
+ // wait for any other read to complete
+// TODO: set up a waiting thread instead of a loop
+ bool bWait = true;
+ do
+ {
+ rMutex.maMutex.acquire();
+ bWait = (rMutex.nReadCount != 0);
+ rMutex.maMutex.release();
+ } while ( bWait );
+ }
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/resmgr.cxx b/unotools/source/i18n/resmgr.cxx
new file mode 100644
index 000000000..a1d74ec1b
--- /dev/null
+++ b/unotools/source/i18n/resmgr.cxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 .
+ */
+
+// Needed when #include <boost/locale.hpp> below includes Boost 1.65.1
+// workdir/UnpackedTarball/boost/boost/locale/format.hpp using "std::auto_ptr<data> d;", but must
+// come very early here in case <memory> is already (indirectly) included earlier:
+#include <config_libcxx.h>
+#if HAVE_LIBCXX
+#define _LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR
+#elif defined _MSC_VER
+#define _HAS_AUTO_PTR_ETC 1
+#endif
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <string.h>
+#include <stdio.h>
+#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
+# include <libintl.h>
+#endif
+
+#include <comphelper/lok.hxx>
+#include <unotools/resmgr.hxx>
+#include <osl/thread.h>
+#include <osl/file.hxx>
+#include <rtl/crc.h>
+#include <rtl/bootstrap.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <boost/locale.hpp>
+#include <boost/locale/gnu_gettext.hpp>
+
+#include <unordered_map>
+#include <memory>
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+#if defined(_WIN32) && defined(DBG_UTIL)
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <prewin.h>
+#include <crtdbg.h>
+#include <postwin.h>
+#endif
+
+namespace
+{
+ OUString createFromUtf8(const char* data, size_t size)
+ {
+ OUString aTarget;
+ bool bSuccess = rtl_convertStringToUString(&aTarget.pData,
+ data,
+ size,
+ RTL_TEXTENCODING_UTF8,
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR|RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR|RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR);
+ (void) bSuccess;
+ assert(bSuccess);
+ return aTarget;
+ }
+
+ OString genKeyId(const OString& rGenerator)
+ {
+ sal_uInt32 nCRC = rtl_crc32(0, rGenerator.getStr(), rGenerator.getLength());
+ // Use simple ASCII characters, exclude I, l, 1 and O, 0 to avoid confusing IDs
+ static const char sSymbols[] =
+ "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
+ char sKeyId[6];
+ for (short nKeyInd = 0; nKeyInd < 5; ++nKeyInd)
+ {
+ sKeyId[nKeyInd] = sSymbols[(nCRC & 63) % strlen(sSymbols)];
+ nCRC >>= 6;
+ }
+ sKeyId[5] = '\0';
+ return sKeyId;
+ }
+}
+
+#if defined(_WIN32) && defined(DBG_UTIL)
+static int IgnoringCrtReportHook(int reportType, wchar_t *message, int * /* returnValue */)
+{
+ OUString sType;
+ if (reportType == _CRT_WARN)
+ sType = "WARN";
+ else if (reportType == _CRT_ERROR)
+ sType = "ERROR";
+ else if (reportType == _CRT_ASSERT)
+ sType = "ASSERT";
+ else
+ sType = "?(" + OUString::number(reportType) + ")";
+
+ SAL_WARN("unotools.i18n", "CRT Report Hook: " << sType << ": " << OUString(o3tl::toU(message)));
+
+ return TRUE;
+}
+#endif
+
+
+namespace Translate
+{
+ std::locale Create(const char* pPrefixName, const LanguageTag& rLocale)
+ {
+ static std::unordered_map<OString, std::locale> aCache;
+ OString sIdentifier = rLocale.getGlibcLocaleString(".UTF-8").toUtf8();
+ OString sUnique = sIdentifier + pPrefixName;
+ auto aFind = aCache.find(sUnique);
+ if (aFind != aCache.end())
+ return aFind->second;
+ boost::locale::generator gen;
+ gen.characters(boost::locale::char_facet);
+ gen.categories(boost::locale::message_facet | boost::locale::information_facet);
+#if defined(ANDROID)
+ OString sPath(OString(lo_get_app_data_dir()) + "/program/resource");
+#else
+ OUString uri("$BRAND_BASE_DIR/$BRAND_SHARE_RESOURCE_SUBDIR/");
+ rtl::Bootstrap::expandMacros(uri);
+ OUString path;
+ osl::File::getSystemPathFromFileURL(uri, path);
+ OString sPath(OUStringToOString(path, osl_getThreadTextEncoding()));
+#endif
+ gen.add_messages_path(sPath.getStr());
+#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
+ // allow gettext to find these .mo files e.g. so gtk dialogs can use them
+ bindtextdomain(pPrefixName, sPath.getStr());
+ // tdf#131069 gtk, and anything sane, always wants utf-8 strings as output
+ bind_textdomain_codeset(pPrefixName, "UTF-8");
+#endif
+ gen.add_messages_domain(pPrefixName);
+
+#if defined(_WIN32) && defined(DBG_UTIL)
+ // With a newer C++ debug runtime (in an --enable-dbgutil build), passing an invalid locale
+ // name causes an attempt to display an error dialog. Which does not even show up, at least
+ // for me, but instead the process (gengal, at least) just hangs. Which is far from ideal.
+
+ // Passing a POSIX-style locale name to the std::locale constructor on Windows is a bit odd,
+ // but apparently in the normal C++ runtime it "just" causes an exception to be thrown, that
+ // boost catches (see the loadable(std::string name) in boost's
+ // libs\locale\src\std\std_backend.cpp), and then instead uses the Windows style locale name
+ // it knows how to construct. (Why does it even try the POSIX style name I can't
+ // understand.)
+
+ // Actually it isn't just the locale name part "en_US" of a locale like "en_US.UTF-8" that
+ // is problematic, but also the encoding part, "UTF-8". The Microsoft C/C++ library does not
+ // support UTF-8 locales. The error message that our own report hook catches says:
+ // "f:\dd\vctools\crt\crtw32\stdcpp\xmbtowc.c(89) : Assertion failed: ploc->_Mbcurmax == 1
+ // || ploc->_Mbcurmax == 2". Clearly in a UTF-8 locale (perhaps one that boost internally
+ // constructs?) the maximum bytes per character will be more than 2.
+
+ // With a debug C++ runtime, we need to avoid the error dialog, and just ignore the error.
+
+ struct CrtSetReportHook
+ {
+ int mnCrtSetReportHookSucceeded;
+
+ CrtSetReportHook()
+ {
+ mnCrtSetReportHookSucceeded = _CrtSetReportHookW2(_CRT_RPTHOOK_INSTALL, IgnoringCrtReportHook);
+ }
+
+ ~CrtSetReportHook()
+ {
+ if (mnCrtSetReportHookSucceeded >= 0)
+ _CrtSetReportHookW2(_CRT_RPTHOOK_REMOVE, IgnoringCrtReportHook);
+ }
+ } aHook;
+
+#endif
+
+ std::locale aRet(gen(sIdentifier.getStr()));
+
+ aCache[sUnique] = aRet;
+ return aRet;
+ }
+
+ OUString get(const char* pContextAndId, const std::locale &loc)
+ {
+ OString sContext;
+ const char *pId = strchr(pContextAndId, '\004');
+ if (!pId)
+ pId = pContextAndId;
+ else
+ {
+ sContext = OString(pContextAndId, pId - pContextAndId);
+ ++pId;
+ assert(!strchr(pId, '\004') && "should be using nget, not get");
+ }
+
+ //if it's a key id locale, generate it here
+ if (std::use_facet<boost::locale::info>(loc).language() == "qtz")
+ {
+ OString sKeyId(genKeyId(OString(pContextAndId).replace('\004', '|')));
+ return OUString::fromUtf8(sKeyId) + u"\u2016" + createFromUtf8(pId, strlen(pId));
+ }
+
+ //otherwise translate it
+ const std::string ret = boost::locale::pgettext(sContext.getStr(), pId, loc);
+ OUString result(ExpandVariables(createFromUtf8(ret.data(), ret.size())));
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // If it is de-CH, change sharp s to double s.
+ if (std::use_facet<boost::locale::info>(loc).country() == "CH" &&
+ std::use_facet<boost::locale::info>(loc).language() == "de")
+ result = result.replaceAll(OUString::fromUtf8("\xC3\x9F"), "ss");
+ }
+ return result;
+ }
+
+ OUString nget(const char* pContextAndIds, int n, const std::locale &loc)
+ {
+ OString sContextIdId(pContextAndIds);
+ std::vector<OString> aContextIdId;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ aContextIdId.push_back(sContextIdId.getToken(0, '\004', nIndex));
+ }
+ while (nIndex >= 0);
+ assert(aContextIdId.size() == 3 && "should be using get, not nget");
+
+ //if it's a key id locale, generate it here
+ if (std::use_facet<boost::locale::info>(loc).language() == "qtz")
+ {
+ OString sKeyId(genKeyId(aContextIdId[0] + "|" + aContextIdId[1]));
+ int nForm = n == 0 ? 1 : 2;
+ return OUString::fromUtf8(sKeyId) + u"\u2016" + createFromUtf8(aContextIdId[nForm].getStr(), aContextIdId[nForm].getLength());
+ }
+
+ //otherwise translate it
+ const std::string ret = boost::locale::npgettext(aContextIdId[0].getStr(), aContextIdId[1].getStr(), aContextIdId[2].getStr(), n, loc);
+ OUString result(ExpandVariables(createFromUtf8(ret.data(), ret.size())));
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (std::use_facet<boost::locale::info>(loc).country() == "CH" &&
+ std::use_facet<boost::locale::info>(loc).language() == "de")
+ result = result.replaceAll(OUString::fromUtf8("\xC3\x9F"), "ss");
+ }
+ return result;
+ }
+
+ static ResHookProc pImplResHookProc = nullptr;
+
+ OUString ExpandVariables(const OUString& rString)
+ {
+ if (pImplResHookProc)
+ return pImplResHookProc(rString);
+ return rString;
+ }
+
+ void SetReadStringHook( ResHookProc pProc )
+ {
+ pImplResHookProc = pProc;
+ }
+
+ ResHookProc GetReadStringHook()
+ {
+ return pImplResHookProc;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/textsearch.cxx b/unotools/source/i18n/textsearch.cxx
new file mode 100644
index 000000000..42b995f70
--- /dev/null
+++ b/unotools/source/i18n/textsearch.cxx
@@ -0,0 +1,401 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <cstdlib>
+#include <string_view>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/searchopt.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <com/sun/star/util/TextSearch2.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/textsearch.hxx>
+#include <rtl/instance.hxx>
+#include <rtl/ustrbuf.hxx>
+
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+namespace utl
+{
+
+SearchParam::SearchParam( const OUString &rText,
+ SearchType eType,
+ bool bCaseSensitive,
+ sal_uInt32 cWildEscChar,
+ bool bWildMatchSel )
+{
+ sSrchStr = rText;
+ m_eSrchType = eType;
+
+ m_cWildEscChar = cWildEscChar;
+
+ m_bCaseSense = bCaseSensitive;
+ m_bWildMatchSel = bWildMatchSel;
+}
+
+SearchParam::SearchParam( const SearchParam& rParam )
+{
+ sSrchStr = rParam.sSrchStr;
+ m_eSrchType = rParam.m_eSrchType;
+
+ m_cWildEscChar = rParam.m_cWildEscChar;
+
+ m_bCaseSense = rParam.m_bCaseSense;
+ m_bWildMatchSel = rParam.m_bWildMatchSel;
+}
+
+SearchParam::~SearchParam() {}
+
+static bool lcl_Equals( const i18nutil::SearchOptions2& rSO1, const i18nutil::SearchOptions2& rSO2 )
+{
+ return
+ rSO1.AlgorithmType2 == rSO2.AlgorithmType2 &&
+ rSO1.WildcardEscapeCharacter == rSO2.WildcardEscapeCharacter &&
+ rSO1.algorithmType == rSO2.algorithmType &&
+ rSO1.searchFlag == rSO2.searchFlag &&
+ rSO1.searchString == rSO2.searchString &&
+ rSO1.replaceString == rSO2.replaceString &&
+ rSO1.changedChars == rSO2.changedChars &&
+ rSO1.deletedChars == rSO2.deletedChars &&
+ rSO1.insertedChars == rSO2.insertedChars &&
+ rSO1.Locale.Language == rSO2.Locale.Language &&
+ rSO1.Locale.Country == rSO2.Locale.Country &&
+ rSO1.Locale.Variant == rSO2.Locale.Variant &&
+ rSO1.transliterateFlags == rSO2.transliterateFlags;
+}
+
+namespace
+{
+ struct CachedTextSearch
+ {
+ ::osl::Mutex mutex;
+ i18nutil::SearchOptions2 Options;
+ css::uno::Reference< css::util::XTextSearch2 > xTextSearch;
+ };
+
+ struct theCachedTextSearch
+ : public rtl::Static< CachedTextSearch, theCachedTextSearch > {};
+}
+
+Reference<XTextSearch2> TextSearch::getXTextSearch( const i18nutil::SearchOptions2& rPara )
+{
+ CachedTextSearch &rCache = theCachedTextSearch::get();
+
+ osl::MutexGuard aGuard(rCache.mutex);
+
+ if ( lcl_Equals(rCache.Options, rPara) )
+ return rCache.xTextSearch;
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ rCache.xTextSearch.set( ::TextSearch2::create(xContext) );
+ rCache.xTextSearch->setOptions2( rPara.toUnoSearchOptions2() );
+ rCache.Options = rPara;
+
+ return rCache.xTextSearch;
+}
+
+TextSearch::TextSearch(const SearchParam & rParam, LanguageType eLang )
+{
+ if( LANGUAGE_NONE == eLang )
+ eLang = LANGUAGE_SYSTEM;
+ css::lang::Locale aLocale( LanguageTag::convertToLocale( eLang ) );
+
+ Init( rParam, aLocale);
+}
+
+TextSearch::TextSearch(const SearchParam & rParam, const CharClass& rCClass )
+{
+ Init( rParam, rCClass.getLanguageTag().getLocale() );
+}
+
+TextSearch::TextSearch( const i18nutil::SearchOptions2& rPara )
+{
+ xTextSearch = getXTextSearch( rPara );
+}
+
+i18nutil::SearchOptions2 TextSearch::UpgradeToSearchOptions2( const i18nutil::SearchOptions& rOptions )
+{
+ sal_Int16 nAlgorithmType2;
+ switch (rOptions.algorithmType)
+ {
+ case SearchAlgorithms_REGEXP:
+ nAlgorithmType2 = SearchAlgorithms2::REGEXP;
+ break;
+ case SearchAlgorithms_APPROXIMATE:
+ nAlgorithmType2 = SearchAlgorithms2::APPROXIMATE;
+ break;
+ case SearchAlgorithms_ABSOLUTE:
+ nAlgorithmType2 = SearchAlgorithms2::ABSOLUTE;
+ break;
+ default:
+ for (;;) std::abort();
+ }
+ // It would be nice if an inherited struct had a ctor that takes an
+ // instance of the object the struct derived from...
+ i18nutil::SearchOptions2 aOptions2(
+ rOptions.algorithmType,
+ rOptions.searchFlag,
+ rOptions.searchString,
+ rOptions.replaceString,
+ rOptions.Locale,
+ rOptions.changedChars,
+ rOptions.deletedChars,
+ rOptions.insertedChars,
+ rOptions.transliterateFlags,
+ nAlgorithmType2,
+ 0 // no wildcard search, no escape character...
+ );
+ return aOptions2;
+}
+
+void TextSearch::Init( const SearchParam & rParam,
+ const css::lang::Locale& rLocale )
+{
+ // convert SearchParam to the UNO SearchOptions2
+ i18nutil::SearchOptions2 aSOpt;
+
+ switch( rParam.GetSrchType() )
+ {
+ case SearchParam::SearchType::Wildcard:
+ aSOpt.AlgorithmType2 = SearchAlgorithms2::WILDCARD;
+ aSOpt.algorithmType = SearchAlgorithms::SearchAlgorithms_MAKE_FIXED_SIZE; // no old enum for that
+ aSOpt.WildcardEscapeCharacter = rParam.GetWildEscChar();
+ if (rParam.IsWildMatchSel())
+ aSOpt.searchFlag |= SearchFlags::WILD_MATCH_SELECTION;
+ break;
+
+ case SearchParam::SearchType::Regexp:
+ aSOpt.AlgorithmType2 = SearchAlgorithms2::REGEXP;
+ aSOpt.algorithmType = SearchAlgorithms_REGEXP;
+ break;
+
+ case SearchParam::SearchType::Normal:
+ aSOpt.AlgorithmType2 = SearchAlgorithms2::ABSOLUTE;
+ aSOpt.algorithmType = SearchAlgorithms_ABSOLUTE;
+ break;
+
+ default:
+ for (;;) std::abort();
+ }
+ aSOpt.searchString = rParam.GetSrchStr();
+ aSOpt.replaceString = "";
+ aSOpt.Locale = rLocale;
+ aSOpt.transliterateFlags = TransliterationFlags::NONE;
+ if( !rParam.IsCaseSensitive() )
+ {
+ aSOpt.searchFlag |= SearchFlags::ALL_IGNORE_CASE;
+ aSOpt.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
+ }
+
+ xTextSearch = getXTextSearch( aSOpt );
+}
+
+void TextSearch::SetLocale( const i18nutil::SearchOptions2& rOptions,
+ const css::lang::Locale& rLocale )
+{
+ i18nutil::SearchOptions2 aSOpt( rOptions );
+ aSOpt.Locale = rLocale;
+
+ xTextSearch = getXTextSearch( aSOpt );
+}
+
+TextSearch::~TextSearch()
+{
+}
+
+/*
+ * General search methods. These methods will call the respective
+ * methods, such as ordinary string searching or regular expression
+ * matching, using the method pointer.
+ */
+bool TextSearch::SearchForward( const OUString &rStr,
+ sal_Int32* pStart, sal_Int32* pEnd,
+ css::util::SearchResult* pRes)
+{
+ bool bRet = false;
+ try
+ {
+ if( xTextSearch.is() )
+ {
+ SearchResult aRet( xTextSearch->searchForward( rStr, *pStart, *pEnd ));
+ if( aRet.subRegExpressions > 0 )
+ {
+ bRet = true;
+ // the XTextsearch returns in startOffset the higher position
+ // and the endposition is always exclusive.
+ // The caller of this function will have in startPos the
+ // lower pos. and end
+ *pStart = aRet.startOffset[ 0 ];
+ *pEnd = aRet.endOffset[ 0 ];
+ if( pRes )
+ *pRes = aRet;
+ }
+ }
+ }
+ catch ( Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "SearchForward: Exception caught!" );
+ }
+ return bRet;
+}
+
+bool TextSearch::searchForward( const OUString &rStr )
+{
+ sal_Int32 pStart = 0;
+ sal_Int32 pEnd = rStr.getLength();
+
+ bool bResult = SearchForward(rStr, &pStart, &pEnd);
+
+ return bResult;
+}
+
+bool TextSearch::SearchBackward( const OUString & rStr, sal_Int32* pStart,
+ sal_Int32* pEnd, SearchResult* pRes )
+{
+ bool bRet = false;
+ try
+ {
+ if( xTextSearch.is() )
+ {
+ SearchResult aRet( xTextSearch->searchBackward( rStr, *pStart, *pEnd ));
+ if( aRet.subRegExpressions )
+ {
+ bRet = true;
+ // the XTextsearch returns in startOffset the higher position
+ // and the endposition is always exclusive.
+ // The caller of this function will have in startPos the
+ // lower pos. and end
+ *pEnd = aRet.startOffset[ 0 ];
+ *pStart = aRet.endOffset[ 0 ];
+ if( pRes )
+ *pRes = aRet;
+ }
+ }
+ }
+ catch ( Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "SearchBackward: Exception caught!" );
+ }
+ return bRet;
+}
+
+void TextSearch::ReplaceBackReferences( OUString& rReplaceStr, const OUString &rStr, const SearchResult& rResult ) const
+{
+ if( rResult.subRegExpressions > 0 )
+ {
+ sal_Unicode sFndChar;
+ sal_Int32 i;
+ OUStringBuffer sBuff(rReplaceStr.getLength()*4);
+ for(i = 0; i < rReplaceStr.getLength(); i++)
+ {
+ if( rReplaceStr[i] == '&')
+ {
+ sal_Int32 nStart = rResult.startOffset[0];
+ sal_Int32 nLength = rResult.endOffset[0] - rResult.startOffset[0];
+ sBuff.append(std::u16string_view(rStr).substr(nStart, nLength));
+ }
+ else if((i < rReplaceStr.getLength() - 1) && rReplaceStr[i] == '$')
+ {
+ sFndChar = rReplaceStr[ i + 1 ];
+ switch(sFndChar)
+ { // placeholder for a backward reference?
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ int j = sFndChar - '0'; // index
+ if(j < rResult.subRegExpressions)
+ {
+ sal_Int32 nSttReg = rResult.startOffset[j];
+ sal_Int32 nRegLen = rResult.endOffset[j];
+ if (nSttReg < 0 || nRegLen < 0) // A "not found" optional capture
+ {
+ nSttReg = nRegLen = 0; // Copy empty string
+ }
+ else if (nRegLen >= nSttReg)
+ {
+ nRegLen = nRegLen - nSttReg;
+ }
+ else
+ {
+ nRegLen = nSttReg - nRegLen;
+ nSttReg = rResult.endOffset[j];
+ }
+ // Copy reference from found string
+ sBuff.append(std::u16string_view(rStr).substr(nSttReg, nRegLen));
+ }
+ i += 1;
+ }
+ break;
+ default:
+ sBuff.append(rReplaceStr[i]);
+ sBuff.append(rReplaceStr[i+1]);
+ i += 1;
+ break;
+ }
+ }
+ else if((i < rReplaceStr.getLength() - 1) && rReplaceStr[i] == '\\')
+ {
+ sFndChar = rReplaceStr[ i+1 ];
+ switch(sFndChar)
+ {
+ case '\\':
+ case '&':
+ case '$':
+ sBuff.append(sFndChar);
+ i+=1;
+ break;
+ case 't':
+ sBuff.append('\t');
+ i += 1;
+ break;
+ default:
+ sBuff.append(rReplaceStr[i]);
+ sBuff.append(rReplaceStr[i+1]);
+ i += 1;
+ break;
+ }
+ }
+ else
+ {
+ sBuff.append(rReplaceStr[i]);
+ }
+ }
+ rReplaceStr = sBuff.makeStringAndClear();
+ }
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/transliterationwrapper.cxx b/unotools/source/i18n/transliterationwrapper.cxx
new file mode 100644
index 000000000..5deb0e5a6
--- /dev/null
+++ b/unotools/source/i18n/transliterationwrapper.cxx
@@ -0,0 +1,232 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/i18n/Transliteration.hpp>
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+using namespace ::utl;
+
+TransliterationWrapper::TransliterationWrapper(
+ const Reference< XComponentContext > & rxContext,
+ TransliterationFlags nTyp )
+ : xTrans( Transliteration::create(rxContext) ),
+ aLanguageTag( LANGUAGE_SYSTEM ), nType( nTyp ), bFirstCall( true )
+{
+}
+
+TransliterationWrapper::~TransliterationWrapper()
+{
+}
+
+OUString TransliterationWrapper::transliterate(const OUString& rStr, LanguageType nLang,
+ sal_Int32 nStart, sal_Int32 nLen,
+ Sequence <sal_Int32>* pOffset )
+{
+ OUString sRet;
+ if( xTrans.is() )
+ {
+ try
+ {
+ loadModuleIfNeeded( nLang );
+
+ if ( pOffset )
+ sRet = xTrans->transliterate( rStr, nStart, nLen, *pOffset );
+ else
+ sRet = xTrans->transliterateString2String( rStr, nStart, nLen);
+ }
+ catch( Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "transliterate: Exception caught!" );
+ }
+ }
+ return sRet;
+}
+
+OUString TransliterationWrapper::transliterate( const OUString& rStr,
+ sal_Int32 nStart, sal_Int32 nLen ) const
+{
+ OUString sRet( rStr );
+ if( xTrans.is() )
+ {
+ try
+ {
+ sRet = xTrans->transliterateString2String( rStr, nStart, nLen);
+ }
+ catch( Exception& )
+ {
+ SAL_WARN( "unotools.i18n", "transliterate: Exception caught!" );
+ }
+ }
+ return sRet;
+}
+
+bool TransliterationWrapper::needLanguageForTheMode() const
+{
+ return TransliterationFlags::UPPERCASE_LOWERCASE == nType ||
+ TransliterationFlags::LOWERCASE_UPPERCASE == nType ||
+ TransliterationFlags::IGNORE_CASE == nType ||
+ TransliterationFlags::SENTENCE_CASE == nType ||
+ TransliterationFlags::TITLE_CASE == nType ||
+ TransliterationFlags::TOGGLE_CASE == nType;
+}
+
+void TransliterationWrapper::setLanguageLocaleImpl( LanguageType nLang )
+{
+ if( LANGUAGE_NONE == nLang )
+ nLang = LANGUAGE_SYSTEM;
+ aLanguageTag.reset( nLang);
+}
+
+void TransliterationWrapper::loadModuleIfNeeded( LanguageType nLang )
+{
+ bool bLoad = bFirstCall;
+ bFirstCall = false;
+
+ if( nType == TransliterationFlags::SENTENCE_CASE )
+ {
+ if( bLoad )
+ loadModuleByImplName("SENTENCE_CASE", nLang);
+ }
+ else if( nType == TransliterationFlags::TITLE_CASE )
+ {
+ if( bLoad )
+ loadModuleByImplName("TITLE_CASE", nLang);
+ }
+ else if( nType == TransliterationFlags::TOGGLE_CASE )
+ {
+ if( bLoad )
+ loadModuleByImplName("TOGGLE_CASE", nLang);
+ }
+ else
+ {
+ if( aLanguageTag.getLanguageType() != nLang )
+ {
+ setLanguageLocaleImpl( nLang );
+ if( !bLoad )
+ bLoad = needLanguageForTheMode();
+ }
+ if( bLoad )
+ loadModuleImpl();
+ }
+}
+
+void TransliterationWrapper::loadModuleImpl() const
+{
+ if ( bFirstCall )
+ const_cast<TransliterationWrapper*>(this)->setLanguageLocaleImpl( LANGUAGE_SYSTEM );
+
+ try
+ {
+ if ( xTrans.is() )
+ xTrans->loadModule( static_cast<TransliterationModules>(nType), aLanguageTag.getLocale() );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "loadModuleImpl" );
+ }
+
+ bFirstCall = false;
+}
+
+void TransliterationWrapper::loadModuleByImplName(const OUString& rModuleName,
+ LanguageType nLang )
+{
+ try
+ {
+ setLanguageLocaleImpl( nLang );
+ css::lang::Locale aLocale( aLanguageTag.getLocale());
+ // Reset LanguageTag, so the next call to loadModuleIfNeeded() forces
+ // new settings.
+ aLanguageTag.reset( LANGUAGE_DONTKNOW);
+ if ( xTrans.is() )
+ xTrans->loadModuleByImplName( rModuleName, aLocale );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "loadModuleByImplName" );
+ }
+
+ bFirstCall = false;
+}
+
+bool TransliterationWrapper::equals(
+ const OUString& rStr1, sal_Int32 nPos1, sal_Int32 nCount1, sal_Int32& nMatch1,
+ const OUString& rStr2, sal_Int32 nPos2, sal_Int32 nCount2, sal_Int32& nMatch2 ) const
+{
+ try
+ {
+ if( bFirstCall )
+ loadModuleImpl();
+ if ( xTrans.is() )
+ return xTrans->equals( rStr1, nPos1, nCount1, nMatch1, rStr2, nPos2, nCount2, nMatch2 );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "equals" );
+ }
+ return false;
+}
+
+sal_Int32 TransliterationWrapper::compareString( const OUString& rStr1, const OUString& rStr2 ) const
+{
+ try
+ {
+ if( bFirstCall )
+ loadModuleImpl();
+ if ( xTrans.is() )
+ return xTrans->compareString( rStr1, rStr2 );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "compareString" );
+ }
+ return 0;
+}
+
+// --- helpers --------------------------------------------------------
+
+bool TransliterationWrapper::isEqual( const OUString& rStr1, const OUString& rStr2 ) const
+{
+ sal_Int32 nMatch1(0), nMatch2(0);
+ bool bMatch = equals(
+ rStr1, 0, rStr1.getLength(), nMatch1,
+ rStr2, 0, rStr2.getLength(), nMatch2 );
+ return bMatch;
+}
+
+bool TransliterationWrapper::isMatch( const OUString& rStr1, const OUString& rStr2 ) const
+{
+ sal_Int32 nMatch1(0), nMatch2(0);
+ equals(
+ rStr1, 0, rStr1.getLength(), nMatch1,
+ rStr2, 0, rStr2.getLength(), nMatch2 );
+ return (nMatch1 <= nMatch2) && (nMatch1 == rStr1.getLength());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */