LibreOffice Module i18npool (master)  1
ordinalsuffix.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
22 #include <sal/log.hxx>
23 #include <comphelper/sequence.hxx>
25 #include <ordinalsuffix.hxx>
26 
27 #include <unicode/rbnf.h>
28 #include <unicode/normlzr.h>
29 #include <memory>
30 
31 namespace com::sun::star::uno { class XComponentContext; }
32 
33 using namespace ::com::sun::star::i18n;
34 using namespace ::com::sun::star::uno;
35 using namespace ::com::sun::star;
36 
37 namespace i18npool {
38 
39 
41 {
42 }
43 
45 {
46 }
47 
48 namespace
49 {
50  OUString mungeUnicodeStringToOUString(const icu::UnicodeString &rIn, UErrorCode &rCode)
51  {
52  // Apply NFKC normalization to get normal letters
53  icu::UnicodeString normalized;
54  icu::Normalizer::normalize(rIn, UNORM_NFKC, 0, normalized, rCode);
55  // Convert the normalized UnicodeString to OUString
56  OUString sRet = (U_SUCCESS(rCode))
57  ? OUString(reinterpret_cast<const sal_Unicode *>(normalized.getBuffer()), normalized.length())
58  : OUString();
59  // replace any minus signs with hyphen-minus so that negative numbers
60  // from the simple number formatter and heavy-duty pattern formatter
61  // agree as to their negative number sign
62  return sRet.replace(0x2212, '-');
63  }
64 }
65 
66 /*
67  * For this method to properly return the ordinal suffix for other locales
68  * than english ones, ICU 4.2+ has to be used.
69  */
70 uno::Sequence< OUString > SAL_CALL OrdinalSuffixService::getOrdinalSuffix( sal_Int32 nNumber,
71  const lang::Locale &rLocale )
72 {
73  uno::Sequence< OUString > retValue;
74 
75  // Get the value from ICU
76  UErrorCode nCode = U_ZERO_ERROR;
77  const icu::Locale aIcuLocale( LanguageTagIcu::getIcuLocale( LanguageTag( rLocale)));
78 
79  icu::RuleBasedNumberFormat formatter(icu::URBNF_ORDINAL, aIcuLocale, nCode);
80  if (!U_SUCCESS(nCode))
81  return retValue;
82 
83  std::unique_ptr<icu::NumberFormat> xNumberFormat(icu::NumberFormat::createInstance(aIcuLocale, nCode));
84  if (!U_SUCCESS(nCode))
85  return retValue;
86 
87  icu::UnicodeString sFormatWithNoOrdinal;
88  icu::Formattable ftmNumber(static_cast<int32_t>(nNumber));
89  icu::FieldPosition icuPosA;
90  xNumberFormat->format(ftmNumber, sFormatWithNoOrdinal, icuPosA, nCode);
91  if (!U_SUCCESS(nCode))
92  return retValue;
93 
94  OUString sValueWithNoOrdinal = mungeUnicodeStringToOUString(sFormatWithNoOrdinal, nCode);
95  if (!U_SUCCESS(nCode))
96  return retValue;
97 
98  int32_t nRuleSets = formatter.getNumberOfRuleSetNames( );
99  std::vector<OUString> retVec;
100  retVec.reserve(nRuleSets);
101  for (int32_t i = 0; i < nRuleSets; ++i)
102  {
103  icu::UnicodeString ruleSet = formatter.getRuleSetName(i);
104 
105  // format the string
106  icu::UnicodeString sFormatWithOrdinal;
107  icu::FieldPosition icuPosB;
108  formatter.format(static_cast<int32_t>(nNumber), ruleSet, sFormatWithOrdinal, icuPosB, nCode);
109 
110  if (!U_SUCCESS(nCode))
111  continue;
112 
113  OUString sValueWithOrdinal = mungeUnicodeStringToOUString(sFormatWithOrdinal, nCode);
114  if (!U_SUCCESS(nCode))
115  continue;
116 
117  // fdo#54486 lets make sure that the ordinal format and the non-ordinal
118  // format match at the start, so that the expectation can be verified
119  // that there is some trailing "ordinal suffix" which can be extracted
120  bool bSimpleOrdinalSuffix = sValueWithOrdinal.startsWith(sValueWithNoOrdinal);
121 
122  SAL_WARN_IF(!bSimpleOrdinalSuffix, "i18npool", "ordinal " <<
123  sValueWithOrdinal << " didn't start with expected " <<
124  sValueWithNoOrdinal << " prefix");
125 
126  if (!bSimpleOrdinalSuffix)
127  continue;
128 
129  // Remove the number to get the prefix
130  sal_Int32 len = sValueWithNoOrdinal.getLength();
131  retVec.push_back(sValueWithOrdinal.copy(len));
132  }
133 
134  return comphelper::containerToSequence(retVec);
135 }
136 
137 constexpr OUStringLiteral cOrdinalSuffix = u"com.sun.star.i18n.OrdinalSuffix";
138 
140 {
141  return cOrdinalSuffix;
142 }
143 
144 sal_Bool SAL_CALL OrdinalSuffixService::supportsService( const OUString& rServiceName)
145 {
146  return cppu::supportsService(this, rServiceName);
147 }
148 
150 {
151  return { cOrdinalSuffix };
152 }
153 
154 }
155 
156 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
158  css::uno::XComponentContext *,
159  css::uno::Sequence<css::uno::Any> const &)
160 {
161  return cppu::acquire(new i18npool::OrdinalSuffixService());
162 }
163 
164 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual ~OrdinalSuffixService() override
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
constexpr OUStringLiteral cOrdinalSuffix
sal_uInt16 nCode
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_i18n_OrdinalSuffix_get_implementation(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &)
int i
static icu::Locale getIcuLocale(const LanguageTag &rLanguageTag)
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
float u
unsigned char sal_Bool
Constant values shared between i18npool and, for example, the number formatter.
virtual css::uno::Sequence< OUString > SAL_CALL getOrdinalSuffix(sal_Int32 nNumber, const css::lang::Locale &rLocale) override
#define SAL_WARN_IF(condition, area, stream)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
virtual OUString SAL_CALL getImplementationName() override
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override