Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <chrlohdl.hxx>
21 : #include <xmloff/xmltoken.hxx>
22 : #include <xmloff/xmluconv.hxx>
23 : #include <unotools/saveopt.hxx>
24 : #include <i18nlangtag/languagetag.hxx>
25 : #include <rtl/ustrbuf.hxx>
26 : #include <com/sun/star/uno/Any.hxx>
27 : #include <com/sun/star/lang/Locale.hpp>
28 :
29 : using namespace ::com::sun::star;
30 : using namespace ::xmloff::token;
31 :
32 : /* TODO-BCP47: this fiddling with Locale is quite ugly and fragile, especially
33 : * for the fo:script temporarily stored in Variant, it would be better to use
34 : * LanguageTagODF but we have that nasty UNO API requirement here.
35 : * => make LanguageTagODF (unpublished) API? */
36 :
37 : // For runtime performance, instead of converting back and forth between
38 : // com::sun::star::Locale and LanguageTag to decide if script or tag are
39 : // needed, this code takes advantage of knowledge about the internal
40 : // representation of BCP 47 language tags in a Locale if present as done in a
41 : // LanguageTag.
42 :
43 20932 : XMLCharLanguageHdl::~XMLCharLanguageHdl()
44 : {
45 : // nothing to do
46 20932 : }
47 :
48 120 : bool XMLCharLanguageHdl::equals( const ::com::sun::star::uno::Any& r1, const ::com::sun::star::uno::Any& r2 ) const
49 : {
50 120 : bool bRet = false;
51 240 : lang::Locale aLocale1, aLocale2;
52 :
53 120 : if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
54 : {
55 120 : bool bEmptyOrScriptVariant1 = (aLocale1.Variant.isEmpty() || aLocale1.Variant[0] == '-');
56 120 : bool bEmptyOrScriptVariant2 = (aLocale2.Variant.isEmpty() || aLocale2.Variant[0] == '-');
57 120 : if (bEmptyOrScriptVariant1 && bEmptyOrScriptVariant2)
58 120 : bRet = ( aLocale1.Language == aLocale2.Language );
59 : else
60 : {
61 0 : OUString aLanguage1, aLanguage2;
62 0 : if (bEmptyOrScriptVariant1)
63 0 : aLanguage1 = aLocale1.Language;
64 : else
65 0 : aLanguage1 = LanguageTag( aLocale1).getLanguage();
66 0 : if (bEmptyOrScriptVariant2)
67 0 : aLanguage2 = aLocale2.Language;
68 : else
69 0 : aLanguage2 = LanguageTag( aLocale2).getLanguage();
70 0 : bRet = ( aLanguage1 == aLanguage2 );
71 : }
72 : }
73 :
74 240 : return bRet;
75 : }
76 :
77 3520 : bool XMLCharLanguageHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
78 : {
79 3520 : lang::Locale aLocale;
80 3520 : rValue >>= aLocale;
81 :
82 3520 : if( !IsXMLToken(rStrImpValue, XML_NONE) )
83 : {
84 3394 : if (aLocale.Variant.isEmpty())
85 3394 : aLocale.Language = rStrImpValue;
86 : else
87 : {
88 0 : if (!aLocale.Language.isEmpty() || aLocale.Variant[0] != '-')
89 : {
90 : SAL_WARN_IF( aLocale.Language != I18NLANGTAG_QLT, "xmloff.style",
91 : "XMLCharLanguageHdl::importXML - attempt to import language twice");
92 : }
93 : else
94 : {
95 0 : aLocale.Variant = rStrImpValue + aLocale.Variant;
96 0 : if (!aLocale.Country.isEmpty())
97 0 : aLocale.Variant += "-" + aLocale.Country;
98 0 : aLocale.Language = I18NLANGTAG_QLT;
99 : }
100 : }
101 : }
102 :
103 3520 : rValue <<= aLocale;
104 3520 : return true;
105 : }
106 :
107 693 : bool XMLCharLanguageHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
108 : {
109 693 : lang::Locale aLocale;
110 693 : if(!(rValue >>= aLocale))
111 0 : return false;
112 :
113 693 : if (aLocale.Variant.isEmpty())
114 693 : rStrExpValue = aLocale.Language;
115 : else
116 : {
117 0 : LanguageTag aLanguageTag( aLocale);
118 0 : OUString aScript, aCountry;
119 0 : aLanguageTag.getIsoLanguageScriptCountry( rStrExpValue, aScript, aCountry);
120 : // Do not write *:language='none' for a non-ISO language with
121 : // *:rfc-language-tag that is written if Variant is not empty. If there
122 : // is no match do not write this attribute at all.
123 0 : if (rStrExpValue.isEmpty())
124 0 : return false;
125 : }
126 :
127 693 : if( rStrExpValue.isEmpty() )
128 0 : rStrExpValue = GetXMLToken( XML_NONE );
129 :
130 693 : return true;
131 : }
132 :
133 20932 : XMLCharScriptHdl::~XMLCharScriptHdl()
134 : {
135 : // nothing to do
136 20932 : }
137 :
138 120 : bool XMLCharScriptHdl::equals( const ::com::sun::star::uno::Any& r1, const ::com::sun::star::uno::Any& r2 ) const
139 : {
140 120 : bool bRet = false;
141 240 : lang::Locale aLocale1, aLocale2;
142 :
143 120 : if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
144 : {
145 120 : bool bEmptyVariant1 = aLocale1.Variant.isEmpty();
146 120 : bool bEmptyVariant2 = aLocale2.Variant.isEmpty();
147 120 : if (bEmptyVariant1 && bEmptyVariant2)
148 120 : bRet = true;
149 0 : else if ((bEmptyVariant1 && !bEmptyVariant2) || (!bEmptyVariant1 && bEmptyVariant2))
150 : ; // stays false
151 : else
152 : {
153 0 : OUString aScript1, aScript2;
154 0 : if (aLocale1.Variant[0] == '-')
155 0 : aScript1 = aLocale1.Variant.copy(1);
156 : else
157 0 : aScript1 = LanguageTag( aLocale1).getScript();
158 0 : if (aLocale2.Variant[0] == '-')
159 0 : aScript2 = aLocale2.Variant.copy(1);
160 : else
161 0 : aScript2 = LanguageTag( aLocale2).getScript();
162 0 : bRet = ( aScript1 == aScript2 );
163 : }
164 : }
165 :
166 240 : return bRet;
167 : }
168 :
169 0 : bool XMLCharScriptHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
170 : {
171 0 : lang::Locale aLocale;
172 0 : rValue >>= aLocale;
173 :
174 0 : if( !IsXMLToken( rStrImpValue, XML_NONE ) )
175 : {
176 : // Import the script only if we don't have a full BCP 47 language tag
177 : // in Variant yet.
178 0 : if (aLocale.Variant.isEmpty())
179 : {
180 0 : if (aLocale.Language.isEmpty())
181 : {
182 : SAL_INFO( "xmloff.style", "XMLCharScriptHdl::importXML - script but no language yet");
183 : // Temporarily store in Variant and hope the best (we will get
184 : // a language later, yes?)
185 0 : aLocale.Variant = "-" + rStrImpValue;
186 : }
187 : else
188 : {
189 0 : aLocale.Variant = aLocale.Language + "-" + rStrImpValue;
190 0 : if (!aLocale.Country.isEmpty())
191 0 : aLocale.Variant += "-" + aLocale.Country;
192 0 : aLocale.Language = I18NLANGTAG_QLT;
193 : }
194 : }
195 0 : else if (aLocale.Variant[0] == '-')
196 : {
197 : SAL_WARN( "xmloff.style", "XMLCharScriptHdl::importXML - attempt to insert script twice: "
198 : << rStrImpValue << " -> " << aLocale.Variant);
199 : }
200 : else
201 : {
202 : // Assume that if there already is a script or anything else BCP 47
203 : // it was read by XMLCharRfcLanguageTagHdl() and takes precedence.
204 : // On the other hand, an *:rfc-language-tag without script and a
205 : // *:script ?!?
206 : #if OSL_DEBUG_LEVEL > 0 || defined(DBG_UTIL)
207 : LanguageTag aLanguageTag( aLocale);
208 : if (!aLanguageTag.hasScript())
209 : {
210 : SAL_WARN( "xmloff.style", "XMLCharScriptHdl::importXML - attempt to insert script over bcp47: "
211 : << rStrImpValue << " -> " << aLanguageTag.getBcp47());
212 : }
213 : #endif
214 : }
215 : }
216 :
217 0 : rValue <<= aLocale;
218 0 : return true;
219 : }
220 :
221 693 : bool XMLCharScriptHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
222 : {
223 693 : lang::Locale aLocale;
224 693 : if(!(rValue >>= aLocale))
225 0 : return false;
226 :
227 : // Do not write script='none' for default script.
228 :
229 693 : if (aLocale.Variant.isEmpty())
230 693 : return false;
231 :
232 0 : LanguageTag aLanguageTag( aLocale);
233 0 : if (!aLanguageTag.hasScript())
234 0 : return false;
235 :
236 0 : if (SvtSaveOptions().GetODFDefaultVersion() < SvtSaveOptions::ODFVER_012)
237 0 : return false;
238 :
239 0 : OUString aLanguage, aCountry;
240 0 : aLanguageTag.getIsoLanguageScriptCountry( aLanguage, rStrExpValue, aCountry);
241 : // For non-ISO language it does not make sense to write *:script if
242 : // *:language is not written either, does it? It's all in
243 : // *:rfc-language-tag
244 0 : if (aLanguage.isEmpty() || rStrExpValue.isEmpty())
245 0 : return false;
246 :
247 693 : return true;
248 : }
249 :
250 20932 : XMLCharCountryHdl::~XMLCharCountryHdl()
251 : {
252 : // nothing to do
253 20932 : }
254 :
255 120 : bool XMLCharCountryHdl::equals( const ::com::sun::star::uno::Any& r1, const ::com::sun::star::uno::Any& r2 ) const
256 : {
257 120 : bool bRet = false;
258 240 : lang::Locale aLocale1, aLocale2;
259 :
260 120 : if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
261 120 : bRet = ( aLocale1.Country == aLocale2.Country );
262 :
263 240 : return bRet;
264 : }
265 :
266 3518 : bool XMLCharCountryHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
267 : {
268 3518 : lang::Locale aLocale;
269 3518 : rValue >>= aLocale;
270 :
271 3518 : if( !IsXMLToken( rStrImpValue, XML_NONE ) )
272 : {
273 3220 : if (aLocale.Country.isEmpty())
274 : {
275 3220 : aLocale.Country = rStrImpValue;
276 3220 : if (aLocale.Variant.getLength() >= 7 && aLocale.Language == I18NLANGTAG_QLT)
277 : {
278 : // already assembled language tag, at least ll-Ssss and not
279 : // ll-CC or lll-CC
280 0 : sal_Int32 i = aLocale.Variant.indexOf('-'); // separator to script
281 0 : if (2 <= i && i < aLocale.Variant.getLength())
282 : {
283 0 : i = aLocale.Variant.indexOf( '-', i+1);
284 0 : if (i < 0) // no other separator
285 0 : aLocale.Variant += "-" + rStrImpValue; // append country
286 : }
287 : }
288 : }
289 : }
290 :
291 3518 : rValue <<= aLocale;
292 3518 : return true;
293 : }
294 :
295 693 : bool XMLCharCountryHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
296 : {
297 693 : lang::Locale aLocale;
298 693 : if(!(rValue >>= aLocale))
299 0 : return false;
300 :
301 693 : if (aLocale.Variant.isEmpty())
302 693 : rStrExpValue = aLocale.Country;
303 : else
304 : {
305 0 : LanguageTag aLanguageTag( aLocale);
306 0 : OUString aLanguage, aScript;
307 0 : aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, rStrExpValue);
308 : // Do not write *:country='none' for a non-ISO country with
309 : // *:rfc-language-tag that is written if Variant is not empty. If there
310 : // is no match do not write this attribute at all.
311 0 : if (rStrExpValue.isEmpty())
312 0 : return false;
313 : }
314 :
315 693 : if( rStrExpValue.isEmpty() )
316 17 : rStrExpValue = GetXMLToken( XML_NONE );
317 :
318 693 : return true;
319 : }
320 :
321 20932 : XMLCharRfcLanguageTagHdl::~XMLCharRfcLanguageTagHdl()
322 : {
323 : // nothing to do
324 20932 : }
325 :
326 120 : bool XMLCharRfcLanguageTagHdl::equals( const ::com::sun::star::uno::Any& r1, const ::com::sun::star::uno::Any& r2 ) const
327 : {
328 120 : bool bRet = false;
329 240 : lang::Locale aLocale1, aLocale2;
330 :
331 120 : if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
332 120 : bRet = ( aLocale1.Variant == aLocale2.Variant );
333 :
334 240 : return bRet;
335 : }
336 :
337 0 : bool XMLCharRfcLanguageTagHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
338 : {
339 0 : lang::Locale aLocale;
340 0 : rValue >>= aLocale;
341 :
342 0 : if( !IsXMLToken( rStrImpValue, XML_NONE ) )
343 : {
344 0 : aLocale.Variant = rStrImpValue;
345 0 : aLocale.Language = I18NLANGTAG_QLT;
346 : }
347 :
348 0 : rValue <<= aLocale;
349 0 : return true;
350 : }
351 :
352 693 : bool XMLCharRfcLanguageTagHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
353 : {
354 693 : lang::Locale aLocale;
355 693 : if(!(rValue >>= aLocale))
356 0 : return false;
357 :
358 : // Do not write rfc-language-tag='none' if BCP 47 is not needed.
359 693 : if (aLocale.Variant.isEmpty())
360 693 : return false;
361 :
362 0 : if (SvtSaveOptions().GetODFDefaultVersion() < SvtSaveOptions::ODFVER_012)
363 0 : return false;
364 :
365 0 : rStrExpValue = aLocale.Variant;
366 :
367 0 : return true;
368 : }
369 :
370 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|