LibreOffice Module xmloff (master) 1
chrlohdl.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
20#include "chrlohdl.hxx"
21#include <xmloff/xmltoken.hxx>
22#include <xmloff/xmluconv.hxx>
23#include <unotools/saveopt.hxx>
25#include <sal/log.hxx>
26#include <com/sun/star/uno/Any.hxx>
27#include <com/sun/star/lang/Locale.hpp>
28
29using namespace ::com::sun::star;
30using 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// css::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
44{
45 // nothing to do
46}
47
48bool XMLCharLanguageHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
49{
50 bool bRet = false;
51 lang::Locale aLocale1, aLocale2;
52
53 if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
54 {
55 bool bEmptyOrScriptVariant1 = (aLocale1.Variant.isEmpty() || aLocale1.Variant[0] == '-');
56 bool bEmptyOrScriptVariant2 = (aLocale2.Variant.isEmpty() || aLocale2.Variant[0] == '-');
57 if (bEmptyOrScriptVariant1 && bEmptyOrScriptVariant2)
58 bRet = ( aLocale1.Language == aLocale2.Language );
59 else
60 {
61 OUString aLanguage1, aLanguage2;
62 if (bEmptyOrScriptVariant1)
63 aLanguage1 = aLocale1.Language;
64 else
65 aLanguage1 = LanguageTag( aLocale1).getLanguage();
66 if (bEmptyOrScriptVariant2)
67 aLanguage2 = aLocale2.Language;
68 else
69 aLanguage2 = LanguageTag( aLocale2).getLanguage();
70 bRet = ( aLanguage1 == aLanguage2 );
71 }
72 }
73
74 return bRet;
75}
76
77bool XMLCharLanguageHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
78{
79 lang::Locale aLocale;
80 rValue >>= aLocale;
81
82 if( !IsXMLToken(rStrImpValue, XML_NONE) )
83 {
84 if (aLocale.Variant.isEmpty())
85 aLocale.Language = rStrImpValue;
86 else
87 {
88 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 aLocale.Variant = rStrImpValue + aLocale.Variant;
96 if (!aLocale.Country.isEmpty())
97 aLocale.Variant += "-" + aLocale.Country;
98 aLocale.Language = I18NLANGTAG_QLT;
99 }
100 }
101 }
102
103 rValue <<= aLocale;
104 return true;
105}
106
107bool XMLCharLanguageHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
108{
109 lang::Locale aLocale;
110 if(!(rValue >>= aLocale))
111 return false;
112
113 if (aLocale.Variant.isEmpty())
114 rStrExpValue = aLocale.Language;
115 else
116 {
117 LanguageTag aLanguageTag( aLocale);
118 OUString aScript, aCountry;
119 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 if (rStrExpValue.isEmpty())
124 return false;
125 }
126
127 if( rStrExpValue.isEmpty() )
128 rStrExpValue = GetXMLToken( XML_NONE );
129
130 return true;
131}
132
134{
135 // nothing to do
136}
137
138bool XMLCharScriptHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
139{
140 bool bRet = false;
141 lang::Locale aLocale1, aLocale2;
142
143 if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
144 {
145 bool bEmptyVariant1 = aLocale1.Variant.isEmpty();
146 bool bEmptyVariant2 = aLocale2.Variant.isEmpty();
147 if (bEmptyVariant1 && bEmptyVariant2)
148 bRet = true;
149 else if (bEmptyVariant1 != bEmptyVariant2)
150 ; // stays false
151 else
152 {
153 OUString aScript1, aScript2;
154 if (aLocale1.Variant[0] == '-')
155 aScript1 = aLocale1.Variant.copy(1);
156 else
157 aScript1 = LanguageTag( aLocale1).getScript();
158 if (aLocale2.Variant[0] == '-')
159 aScript2 = aLocale2.Variant.copy(1);
160 else
161 aScript2 = LanguageTag( aLocale2).getScript();
162 bRet = ( aScript1 == aScript2 );
163 }
164 }
165
166 return bRet;
167}
168
169bool XMLCharScriptHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
170{
171 lang::Locale aLocale;
172 rValue >>= aLocale;
173
174 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 if (aLocale.Variant.isEmpty())
179 {
180 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 aLocale.Variant = "-" + rStrImpValue;
186 }
187 else
188 {
189 aLocale.Variant = aLocale.Language + "-" + rStrImpValue;
190 if (!aLocale.Country.isEmpty())
191 aLocale.Variant += "-" + aLocale.Country;
192 aLocale.Language = I18NLANGTAG_QLT;
193 }
194 }
195 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 rValue <<= aLocale;
218 return true;
219}
220
221bool XMLCharScriptHdl::exportXML(OUString& rStrExpValue,
222 const uno::Any& rValue, const SvXMLUnitConverter& rUnitConv) const
223{
224 lang::Locale aLocale;
225 if(!(rValue >>= aLocale))
226 return false;
227
228 // Do not write script='none' for default script.
229
230 if (aLocale.Variant.isEmpty())
231 return false;
232
233 LanguageTag aLanguageTag( aLocale);
234 if (!aLanguageTag.hasScript())
235 return false;
236
238 return false;
239
240 OUString aLanguage, aCountry;
241 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, rStrExpValue, aCountry);
242 // For non-ISO language it does not make sense to write *:script if
243 // *:language is not written either, does it? It's all in
244 // *:rfc-language-tag
245 return !aLanguage.isEmpty() && !rStrExpValue.isEmpty();
246}
247
249{
250 // nothing to do
251}
252
253bool XMLCharCountryHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
254{
255 bool bRet = false;
256 lang::Locale aLocale1, aLocale2;
257
258 if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
259 bRet = ( aLocale1.Country == aLocale2.Country );
260
261 return bRet;
262}
263
264bool XMLCharCountryHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
265{
266 lang::Locale aLocale;
267 rValue >>= aLocale;
268
269 if( !IsXMLToken( rStrImpValue, XML_NONE ) )
270 {
271 if (aLocale.Country.isEmpty())
272 {
273 aLocale.Country = rStrImpValue;
274 if (aLocale.Variant.getLength() >= 7 && aLocale.Language == I18NLANGTAG_QLT)
275 {
276 // already assembled language tag, at least ll-Ssss and not
277 // ll-CC or lll-CC
278 sal_Int32 i = aLocale.Variant.indexOf('-'); // separator to script
279 if (2 <= i && i < aLocale.Variant.getLength())
280 {
281 i = aLocale.Variant.indexOf( '-', i+1);
282 if (i < 0) // no other separator
283 aLocale.Variant += "-" + rStrImpValue; // append country
284 }
285 }
286 }
287 }
288
289 rValue <<= aLocale;
290 return true;
291}
292
293bool XMLCharCountryHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
294{
295 lang::Locale aLocale;
296 if(!(rValue >>= aLocale))
297 return false;
298
299 if (aLocale.Variant.isEmpty())
300 rStrExpValue = aLocale.Country;
301 else
302 {
303 LanguageTag aLanguageTag( aLocale);
304 OUString aLanguage, aScript;
305 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, rStrExpValue);
306 // Do not write *:country='none' for a non-ISO country with
307 // *:rfc-language-tag that is written if Variant is not empty. If there
308 // is no match do not write this attribute at all.
309 if (rStrExpValue.isEmpty())
310 return false;
311 }
312
313 if( rStrExpValue.isEmpty() )
314 rStrExpValue = GetXMLToken( XML_NONE );
315
316 return true;
317}
318
320{
321 // nothing to do
322}
323
324bool XMLCharRfcLanguageTagHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
325{
326 bool bRet = false;
327 lang::Locale aLocale1, aLocale2;
328
329 if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
330 bRet = ( aLocale1.Variant == aLocale2.Variant );
331
332 return bRet;
333}
334
335bool XMLCharRfcLanguageTagHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
336{
337 lang::Locale aLocale;
338 rValue >>= aLocale;
339
340 if( !IsXMLToken( rStrImpValue, XML_NONE ) )
341 {
342 // Stored may be a *:rfc-language-tag in violation of ODF v1.3
343 // 19.516 style:rfc-language-tag "It shall only be used if its value
344 // cannot be expressed as a valid combination of the fo:language
345 // 19.871, fo:script 19.242 and fo:country 19.234 attributes".
346 // That could override a more detailed fo:* and we also don't want an
347 // unjustified I18NLANGTAG_QLT extended locale tag, but fetch the
348 // values in case fo:* doesn't follow.
349 // Rule out the obvious.
350 if (rStrImpValue.getLength() < 7)
351 {
352 SAL_WARN("xmloff.style","rfc-language-tag too short: {" << rStrImpValue << "} Set: "
353 << aLocale.Language <<","<< aLocale.Country <<","<< aLocale.Variant);
354 // Ignore empty and keep Ssss or any earlier qlt already set.
355 if (!rStrImpValue.isEmpty() && aLocale.Language != I18NLANGTAG_QLT)
356 {
357 // Shorter than ll-Ssss, so try ll-CC or lll-CC or ll or lll
358 sal_Int32 h = rStrImpValue.indexOf('-');
359 OUString aLang;
360 if (2 <= h && h <= 3)
361 aLang = rStrImpValue.copy(0, h);
362 else if (h < 0 && 2 <= rStrImpValue.getLength() && rStrImpValue.getLength() <= 3)
363 aLang = rStrImpValue;
364 OUString aCoun;
365 if (!aLang.isEmpty() && aLang.getLength() + 3 == rStrImpValue.getLength())
366 aCoun = rStrImpValue.copy( aLang.getLength() + 1);
367 // Ignore identical value or less information.
368 if ((!aLang.isEmpty() && aLang != aLocale.Language) ||
369 (!aCoun.isEmpty() && aCoun != aLocale.Country))
370 {
371 // Do not override existing values.
372 if (aLocale.Language.isEmpty())
373 aLocale.Language = aLang;
374 if (aLocale.Country.isEmpty())
375 aLocale.Country = aCoun;
376 if (aLang != aLocale.Language || aCoun != aLocale.Country)
377 {
378 // No match, so we still need the qlt anyway. Whatever..
379 aLocale.Variant = rStrImpValue;
380 aLocale.Language = I18NLANGTAG_QLT;
381 }
382 }
383 else if (aLang.isEmpty() && aCoun.isEmpty())
384 {
385 // Both empty, some other tag.
386 aLocale.Variant = rStrImpValue;
387 aLocale.Language = I18NLANGTAG_QLT;
388 }
389 }
390 SAL_WARN("xmloff.style","rfc-language-tag too short: now set: "
391 << aLocale.Language <<","<< aLocale.Country <<","<< aLocale.Variant);
392 }
393 else
394 {
395 aLocale.Variant = rStrImpValue;
396 aLocale.Language = I18NLANGTAG_QLT;
397 }
398 }
399
400 rValue <<= aLocale;
401 return true;
402}
403
404bool XMLCharRfcLanguageTagHdl::exportXML(OUString& rStrExpValue,
405 const uno::Any& rValue, const SvXMLUnitConverter& rUnitConv) const
406{
407 lang::Locale aLocale;
408 if(!(rValue >>= aLocale))
409 return false;
410
411 // Do not write rfc-language-tag='none' if BCP 47 is not needed.
412 if (aLocale.Variant.isEmpty())
413 return false;
414
416 return false;
417
418 rStrExpValue = aLocale.Variant;
419
420 return true;
421}
422
423/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString getLanguage() const
OUString getScript() const
const OUString & getBcp47(bool bResolveSystem=true) const
bool hasScript() const
void getIsoLanguageScriptCountry(OUString &rLanguage, OUString &rScript, OUString &rCountry) const
the SvXMLTypeConverter converts values of various types from their internal representation to the tex...
Definition: xmluconv.hxx:83
SvtSaveOptions::ODFSaneDefaultVersion getSaneDefaultVersion() const
ODF version, only when exporting.
Definition: xmluconv.cxx:125
virtual bool exportXML(OUString &rStrExpValue, const css::uno::Any &rValue, const SvXMLUnitConverter &rUnitConverter) const override
Exports the given value according to the XML-data-type corresponding to the derived class.
Definition: chrlohdl.cxx:293
virtual bool equals(const css::uno::Any &r1, const css::uno::Any &r2) const override
Compares two Any's in case of the given XML-data-type.
Definition: chrlohdl.cxx:253
virtual ~XMLCharCountryHdl() override
Definition: chrlohdl.cxx:248
virtual bool importXML(const OUString &rStrImpValue, css::uno::Any &rValue, const SvXMLUnitConverter &rUnitConverter) const override
Imports the given value according to the XML-data-type corresponding to the derived class.
Definition: chrlohdl.cxx:264
virtual bool exportXML(OUString &rStrExpValue, const css::uno::Any &rValue, const SvXMLUnitConverter &rUnitConverter) const override
Exports the given value according to the XML-data-type corresponding to the derived class.
Definition: chrlohdl.cxx:107
virtual bool equals(const css::uno::Any &r1, const css::uno::Any &r2) const override
Compares two Any's in case of the given XML-data-type.
Definition: chrlohdl.cxx:48
virtual ~XMLCharLanguageHdl() override
Definition: chrlohdl.cxx:43
virtual bool importXML(const OUString &rStrImpValue, css::uno::Any &rValue, const SvXMLUnitConverter &rUnitConverter) const override
Imports the given value according to the XML-data-type corresponding to the derived class.
Definition: chrlohdl.cxx:77
virtual bool exportXML(OUString &rStrExpValue, const css::uno::Any &rValue, const SvXMLUnitConverter &rUnitConverter) const override
Exports the given value according to the XML-data-type corresponding to the derived class.
Definition: chrlohdl.cxx:404
virtual bool importXML(const OUString &rStrImpValue, css::uno::Any &rValue, const SvXMLUnitConverter &rUnitConverter) const override
Imports the given value according to the XML-data-type corresponding to the derived class.
Definition: chrlohdl.cxx:335
virtual bool equals(const css::uno::Any &r1, const css::uno::Any &r2) const override
Compares two Any's in case of the given XML-data-type.
Definition: chrlohdl.cxx:324
virtual ~XMLCharRfcLanguageTagHdl() override
Definition: chrlohdl.cxx:319
virtual ~XMLCharScriptHdl() override
Definition: chrlohdl.cxx:133
virtual bool importXML(const OUString &rStrImpValue, css::uno::Any &rValue, const SvXMLUnitConverter &rUnitConverter) const override
Imports the given value according to the XML-data-type corresponding to the derived class.
Definition: chrlohdl.cxx:169
virtual bool exportXML(OUString &rStrExpValue, const css::uno::Any &rValue, const SvXMLUnitConverter &rUnitConverter) const override
Exports the given value according to the XML-data-type corresponding to the derived class.
Definition: chrlohdl.cxx:221
virtual bool equals(const css::uno::Any &r1, const css::uno::Any &r2) const override
Compares two Any's in case of the given XML-data-type.
Definition: chrlohdl.cxx:138
constexpr OUStringLiteral I18NLANGTAG_QLT
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
int i
sal_Int32 h
Handling of tokens in XML:
bool IsXMLToken(std::u16string_view rString, enum XMLTokenEnum eToken)
compare eToken to the string
Definition: xmltoken.cxx:3597
const OUString & GetXMLToken(enum XMLTokenEnum eToken)
return the OUString representation for eToken
Definition: xmltoken.cxx:3541