xref: /core/include/svl/ondemand.hxx (revision ec91e33e)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #pragma once
21 
22 #include <com/sun/star/uno/Reference.hxx>
23 #include <i18nlangtag/lang.h>
24 #include <i18nutil/transliteration.hxx>
25 #include <unotools/syslocale.hxx>
26 #include <unotools/localedatawrapper.hxx>
27 #include <unotools/calendarwrapper.hxx>
28 #include <unotools/transliterationwrapper.hxx>
29 #include <unotools/nativenumberwrapper.hxx>
30 #include <unotools/charclass.hxx>
31 #include <optional>
32 
33 /*
34     On demand instantiation and initialization of several i18n wrappers,
35     helping the number formatter to not perform worse than it already does.
36  */
37 
38 /** @short
39     Switch between LANGUAGE_SYSTEM and LANGUAGE_ENGLISH_US and any other
40     LocaleDataWrapper.
41     SvNumberformatter uses it upon switching locales.
42 
43     @descr
44     Avoids reloading and analysing of locale data again and again.
45 
46     @ATTENTION
47     If the default ctor is used the init() method MUST be called before
48     accessing any locale data. The passed parameters Locale and LanguageType
49     must match each other.
50  */
51 
52 class OnDemandLocaleDataWrapper
53 {
54     css::uno::Reference<css::uno::XComponentContext> m_xContext;
55     SvtSysLocale aSysLocale;
56     LanguageType eCurrentLanguage;
57     LanguageType eLastAnyLanguage;
58     std::optional<LocaleDataWrapper> moEnglish;
59     std::optional<LocaleDataWrapper> moAny;
60     int nCurrent; // 0 == system, 1 == english, 2 == any
61     bool bInitialized;
62 
63 public:
OnDemandLocaleDataWrapper()64     OnDemandLocaleDataWrapper()
65         : eLastAnyLanguage(LANGUAGE_DONTKNOW)
66         , nCurrent(0)
67         , bInitialized(false)
68     {
69         eCurrentLanguage = LANGUAGE_SYSTEM;
70     }
71 
isInitialized() const72     bool isInitialized() const { return bInitialized; }
73 
init(const css::uno::Reference<css::uno::XComponentContext> & rxContext,const LanguageTag & rLanguageTag)74     void init(const css::uno::Reference<css::uno::XComponentContext>& rxContext,
75               const LanguageTag& rLanguageTag)
76     {
77         m_xContext = rxContext;
78         changeLocale(rLanguageTag);
79         bInitialized = true;
80     }
81 
changeLocale(const LanguageTag & rLanguageTag)82     void changeLocale(const LanguageTag& rLanguageTag)
83     {
84         LanguageType eLang = rLanguageTag.getLanguageType(false);
85         if (eLang == LANGUAGE_SYSTEM)
86             nCurrent = 0;
87         else if (eLang == LANGUAGE_ENGLISH_US)
88         {
89             if (!moEnglish)
90                 moEnglish.emplace(m_xContext, rLanguageTag);
91             nCurrent = 1;
92         }
93         else
94         {
95             if (!moAny)
96             {
97                 moAny.emplace(m_xContext, rLanguageTag);
98                 eLastAnyLanguage = eLang;
99             }
100             else if (eLastAnyLanguage != eLang)
101             {
102                 moAny.emplace(m_xContext, rLanguageTag);
103                 eLastAnyLanguage = eLang;
104             }
105             nCurrent = 2;
106         }
107         eCurrentLanguage = eLang;
108     }
109 
getCurrentLanguage() const110     LanguageType getCurrentLanguage() const { return eCurrentLanguage; }
111 
get() const112     const LocaleDataWrapper* get() const
113     {
114         switch (nCurrent)
115         {
116             case 0:
117                 return &aSysLocale.GetLocaleData();
118             case 1:
119                 return &*moEnglish;
120             case 2:
121                 return &*moAny;
122             default:
123                 assert(false);
124                 return nullptr;
125         }
126     }
operator ->() const127     const LocaleDataWrapper* operator->() const { return get(); }
operator *() const128     const LocaleDataWrapper& operator*() const { return *get(); }
129 };
130 
131 /** Load a calendar only if it's needed. Keep calendar for "en-US" locale
132     separately, as there can be alternation between locale dependent and
133     locale independent formats.
134     SvNumberformatter uses it upon switching locales.
135 
136     @ATTENTION If the default ctor is used the init() method MUST be called
137     before accessing the calendar.
138  */
139 class OnDemandCalendarWrapper
140 {
141     css::uno::Reference<css::uno::XComponentContext> m_xContext;
142     css::lang::Locale aEnglishLocale;
143     css::lang::Locale aLocale;
144     mutable css::lang::Locale aLastAnyLocale;
145     mutable std::optional<CalendarWrapper> moEnglish;
146     mutable std::optional<CalendarWrapper> moAny;
147 
148 public:
OnDemandCalendarWrapper()149     OnDemandCalendarWrapper()
150     {
151         LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
152         aEnglishLocale = aEnglishLanguageTag.getLocale();
153         aLastAnyLocale = aEnglishLocale;
154     }
155 
init(const css::uno::Reference<css::uno::XComponentContext> & rxContext,const css::lang::Locale & rLocale)156     void init(const css::uno::Reference<css::uno::XComponentContext>& rxContext,
157               const css::lang::Locale& rLocale)
158     {
159         m_xContext = rxContext;
160         changeLocale(rLocale);
161         moEnglish.reset();
162         moAny.reset();
163     }
164 
changeLocale(const css::lang::Locale & rLocale)165     void changeLocale(const css::lang::Locale& rLocale) { aLocale = rLocale; }
166 
get() const167     CalendarWrapper* get() const
168     {
169         CalendarWrapper* pPtr;
170         if (aLocale == aEnglishLocale)
171         {
172             if (!moEnglish)
173             {
174                 moEnglish.emplace(m_xContext);
175                 moEnglish->loadDefaultCalendar(aEnglishLocale);
176             }
177             pPtr = &*moEnglish;
178         }
179         else
180         {
181             if (!moAny)
182             {
183                 moAny.emplace(m_xContext);
184                 moAny->loadDefaultCalendar(aLocale);
185                 aLastAnyLocale = aLocale;
186             }
187             else if (aLocale != aLastAnyLocale)
188             {
189                 moAny->loadDefaultCalendar(aLocale);
190                 aLastAnyLocale = aLocale;
191             }
192             pPtr = &*moAny;
193         }
194         return pPtr;
195     }
196 };
197 
198 /** Load a transliteration only if it's needed.
199     SvNumberformatter uses it upon switching locales.
200     @ATTENTION If the default ctor is used the init() method MUST be called
201     before accessing the transliteration.
202  */
203 class OnDemandTransliterationWrapper
204 {
205     css::uno::Reference<css::uno::XComponentContext> m_xContext;
206     LanguageType eLanguage;
207     TransliterationFlags nType;
208     mutable std::optional<::utl::TransliterationWrapper> moTransliterate;
209     mutable bool bValid;
210     bool bInitialized;
211 
212 public:
OnDemandTransliterationWrapper()213     OnDemandTransliterationWrapper()
214         : eLanguage(LANGUAGE_SYSTEM)
215         , nType(TransliterationFlags::NONE)
216         , bValid(false)
217         , bInitialized(false)
218     {
219     }
220 
isInitialized() const221     bool isInitialized() const { return bInitialized; }
222 
init(const css::uno::Reference<css::uno::XComponentContext> & rxContext,LanguageType eLang)223     void init(const css::uno::Reference<css::uno::XComponentContext>& rxContext, LanguageType eLang)
224     {
225         m_xContext = rxContext;
226         nType = TransliterationFlags::IGNORE_CASE;
227         changeLocale(eLang);
228         moTransliterate.reset();
229         bInitialized = true;
230     }
231 
changeLocale(LanguageType eLang)232     void changeLocale(LanguageType eLang)
233     {
234         bValid = false;
235         eLanguage = eLang;
236     }
237 
get() const238     const ::utl::TransliterationWrapper* get() const
239     {
240         if (!bValid)
241         {
242             if (!moTransliterate)
243                 moTransliterate.emplace(m_xContext, nType);
244             moTransliterate->loadModuleIfNeeded(eLanguage);
245             bValid = true;
246         }
247         return &*moTransliterate;
248     }
249 
operator ->() const250     const ::utl::TransliterationWrapper* operator->() const { return get(); }
251 };
252 
253 /** Load a native number service wrapper only if it's needed.
254     SvNumberformatter uses it.
255  */
256 class OnDemandNativeNumberWrapper
257 {
258     css::uno::Reference<css::uno::XComponentContext> m_xContext;
259     mutable std::optional<NativeNumberWrapper> moNativeNumber;
260 
261 public:
OnDemandNativeNumberWrapper(const css::uno::Reference<css::uno::XComponentContext> & rContext)262     OnDemandNativeNumberWrapper(const css::uno::Reference<css::uno::XComponentContext>& rContext)
263         : m_xContext(rContext)
264     {
265     }
266 
get() const267     NativeNumberWrapper& get() const
268     {
269         if (!moNativeNumber)
270             moNativeNumber.emplace(m_xContext);
271         return *moNativeNumber;
272     }
273 };
274 
275 /** @short
276     SvNumberformatter uses it upon switching locales.
277 
278     @descr
279     Avoids reloading and analysing of locale data again and again.
280 
281     @ATTENTION
282     If the default ctor is used the init() method MUST be called before
283     accessing any locale data.
284  */
285 
286 class OnDemandCharClass
287 {
288     std::optional<CharClass> moCharClass1;
289     std::optional<CharClass> moCharClass2;
290     int nCurrent; // -1 == uninitialised, 0 == class1, 1 == class2
291 
292 public:
OnDemandCharClass()293     OnDemandCharClass()
294         : nCurrent(-1)
295     {
296     }
297 
changeLocale(const css::uno::Reference<css::uno::XComponentContext> & xContext,const LanguageTag & rLanguageTag)298     void changeLocale(const css::uno::Reference<css::uno::XComponentContext>& xContext,
299                       const LanguageTag& rLanguageTag)
300     {
301         // check for existing match
302         if (moCharClass1 && moCharClass1->getLanguageTag() == rLanguageTag)
303         {
304             nCurrent = 0;
305             return;
306         }
307         if (moCharClass2 && moCharClass2->getLanguageTag() == rLanguageTag)
308         {
309             nCurrent = 1;
310             return;
311         }
312         // no match - update one the current entries
313         if (nCurrent == -1 || nCurrent == 1)
314         {
315             moCharClass1.emplace(xContext, rLanguageTag);
316             nCurrent = 0;
317         }
318         else
319         {
320             moCharClass2.emplace(xContext, rLanguageTag);
321             nCurrent = 1;
322         }
323     }
324 
get() const325     const CharClass* get() const
326     {
327         switch (nCurrent)
328         {
329             case 0:
330                 return &*moCharClass1;
331             case 1:
332                 return &*moCharClass2;
333             default:
334                 assert(false);
335                 return nullptr;
336         }
337     }
operator ->() const338     const CharClass* operator->() const { return get(); }
operator *() const339     const CharClass& operator*() const { return *get(); }
340 };
341 
342 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
343