1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
29 using namespace ::com::sun::star
;
30 using namespace ::xmloff::token
;
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? */
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
43 XMLCharLanguageHdl::~XMLCharLanguageHdl()
48 bool XMLCharLanguageHdl::equals( const ::com::sun::star::uno::Any
& r1
, const ::com::sun::star::uno::Any
& r2
) const
51 lang::Locale aLocale1
, aLocale2
;
53 if( ( r1
>>= aLocale1
) && ( r2
>>= aLocale2
) )
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
);
61 OUString aLanguage1
, aLanguage2
;
62 if (bEmptyOrScriptVariant1
)
63 aLanguage1
= aLocale1
.Language
;
65 aLanguage1
= LanguageTag( aLocale1
).getLanguage();
66 if (bEmptyOrScriptVariant2
)
67 aLanguage2
= aLocale2
.Language
;
69 aLanguage2
= LanguageTag( aLocale2
).getLanguage();
70 bRet
= ( aLanguage1
== aLanguage2
);
77 bool XMLCharLanguageHdl::importXML( const OUString
& rStrImpValue
, uno::Any
& rValue
, const SvXMLUnitConverter
& ) const
82 if( !IsXMLToken(rStrImpValue
, XML_NONE
) )
84 if (aLocale
.Variant
.isEmpty())
85 aLocale
.Language
= rStrImpValue
;
88 if (!aLocale
.Language
.isEmpty() || aLocale
.Variant
[0] != '-')
90 SAL_WARN_IF( aLocale
.Language
!= I18NLANGTAG_QLT
, "xmloff.style",
91 "XMLCharLanguageHdl::importXML - attempt to import language twice");
95 aLocale
.Variant
= rStrImpValue
+ aLocale
.Variant
;
96 if (!aLocale
.Country
.isEmpty())
97 aLocale
.Variant
+= "-" + aLocale
.Country
;
98 aLocale
.Language
= I18NLANGTAG_QLT
;
107 bool XMLCharLanguageHdl::exportXML( OUString
& rStrExpValue
, const uno::Any
& rValue
, const SvXMLUnitConverter
& ) const
109 lang::Locale aLocale
;
110 if(!(rValue
>>= aLocale
))
113 if (aLocale
.Variant
.isEmpty())
114 rStrExpValue
= aLocale
.Language
;
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())
127 if( rStrExpValue
.isEmpty() )
128 rStrExpValue
= GetXMLToken( XML_NONE
);
133 XMLCharScriptHdl::~XMLCharScriptHdl()
138 bool XMLCharScriptHdl::equals( const ::com::sun::star::uno::Any
& r1
, const ::com::sun::star::uno::Any
& r2
) const
141 lang::Locale aLocale1
, aLocale2
;
143 if( ( r1
>>= aLocale1
) && ( r2
>>= aLocale2
) )
145 bool bEmptyVariant1
= aLocale1
.Variant
.isEmpty();
146 bool bEmptyVariant2
= aLocale2
.Variant
.isEmpty();
147 if (bEmptyVariant1
&& bEmptyVariant2
)
149 else if ((bEmptyVariant1
&& !bEmptyVariant2
) || (!bEmptyVariant1
&& bEmptyVariant2
))
153 OUString aScript1
, aScript2
;
154 if (aLocale1
.Variant
[0] == '-')
155 aScript1
= aLocale1
.Variant
.copy(1);
157 aScript1
= LanguageTag( aLocale1
).getScript();
158 if (aLocale2
.Variant
[0] == '-')
159 aScript2
= aLocale2
.Variant
.copy(1);
161 aScript2
= LanguageTag( aLocale2
).getScript();
162 bRet
= ( aScript1
== aScript2
);
169 bool XMLCharScriptHdl::importXML( const OUString
& rStrImpValue
, uno::Any
& rValue
, const SvXMLUnitConverter
& ) const
171 lang::Locale aLocale
;
174 if( !IsXMLToken( rStrImpValue
, XML_NONE
) )
176 // Import the script only if we don't have a full BCP 47 language tag
178 if (aLocale
.Variant
.isEmpty())
180 if (aLocale
.Language
.isEmpty())
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
;
189 aLocale
.Variant
= aLocale
.Language
+ "-" + rStrImpValue
;
190 if (!aLocale
.Country
.isEmpty())
191 aLocale
.Variant
+= "-" + aLocale
.Country
;
192 aLocale
.Language
= I18NLANGTAG_QLT
;
195 else if (aLocale
.Variant
[0] == '-')
197 SAL_WARN( "xmloff.style", "XMLCharScriptHdl::importXML - attempt to insert script twice: "
198 << rStrImpValue
<< " -> " << aLocale
.Variant
);
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
206 #if OSL_DEBUG_LEVEL > 0 || defined(DBG_UTIL)
207 LanguageTag
aLanguageTag( aLocale
);
208 if (!aLanguageTag
.hasScript())
210 SAL_WARN( "xmloff.style", "XMLCharScriptHdl::importXML - attempt to insert script over bcp47: "
211 << rStrImpValue
<< " -> " << aLanguageTag
.getBcp47());
221 bool XMLCharScriptHdl::exportXML( OUString
& rStrExpValue
, const uno::Any
& rValue
, const SvXMLUnitConverter
& ) const
223 lang::Locale aLocale
;
224 if(!(rValue
>>= aLocale
))
227 // Do not write script='none' for default script.
229 if (aLocale
.Variant
.isEmpty())
232 LanguageTag
aLanguageTag( aLocale
);
233 if (!aLanguageTag
.hasScript())
236 if (SvtSaveOptions().GetODFDefaultVersion() < SvtSaveOptions::ODFVER_012
)
239 OUString aLanguage
, aCountry
;
240 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 if (aLanguage
.isEmpty() || rStrExpValue
.isEmpty())
250 XMLCharCountryHdl::~XMLCharCountryHdl()
255 bool XMLCharCountryHdl::equals( const ::com::sun::star::uno::Any
& r1
, const ::com::sun::star::uno::Any
& r2
) const
258 lang::Locale aLocale1
, aLocale2
;
260 if( ( r1
>>= aLocale1
) && ( r2
>>= aLocale2
) )
261 bRet
= ( aLocale1
.Country
== aLocale2
.Country
);
266 bool XMLCharCountryHdl::importXML( const OUString
& rStrImpValue
, uno::Any
& rValue
, const SvXMLUnitConverter
& ) const
268 lang::Locale aLocale
;
271 if( !IsXMLToken( rStrImpValue
, XML_NONE
) )
273 if (aLocale
.Country
.isEmpty())
275 aLocale
.Country
= rStrImpValue
;
276 if (aLocale
.Variant
.getLength() >= 7 && aLocale
.Language
== I18NLANGTAG_QLT
)
278 // already assembled language tag, at least ll-Ssss and not
280 sal_Int32 i
= aLocale
.Variant
.indexOf('-'); // separator to script
281 if (2 <= i
&& i
< aLocale
.Variant
.getLength())
283 i
= aLocale
.Variant
.indexOf( '-', i
+1);
284 if (i
< 0) // no other separator
285 aLocale
.Variant
+= "-" + rStrImpValue
; // append country
295 bool XMLCharCountryHdl::exportXML( OUString
& rStrExpValue
, const uno::Any
& rValue
, const SvXMLUnitConverter
& ) const
297 lang::Locale aLocale
;
298 if(!(rValue
>>= aLocale
))
301 if (aLocale
.Variant
.isEmpty())
302 rStrExpValue
= aLocale
.Country
;
305 LanguageTag
aLanguageTag( aLocale
);
306 OUString aLanguage
, aScript
;
307 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 if (rStrExpValue
.isEmpty())
315 if( rStrExpValue
.isEmpty() )
316 rStrExpValue
= GetXMLToken( XML_NONE
);
321 XMLCharRfcLanguageTagHdl::~XMLCharRfcLanguageTagHdl()
326 bool XMLCharRfcLanguageTagHdl::equals( const ::com::sun::star::uno::Any
& r1
, const ::com::sun::star::uno::Any
& r2
) const
329 lang::Locale aLocale1
, aLocale2
;
331 if( ( r1
>>= aLocale1
) && ( r2
>>= aLocale2
) )
332 bRet
= ( aLocale1
.Variant
== aLocale2
.Variant
);
337 bool XMLCharRfcLanguageTagHdl::importXML( const OUString
& rStrImpValue
, uno::Any
& rValue
, const SvXMLUnitConverter
& ) const
339 lang::Locale aLocale
;
342 if( !IsXMLToken( rStrImpValue
, XML_NONE
) )
344 aLocale
.Variant
= rStrImpValue
;
345 aLocale
.Language
= I18NLANGTAG_QLT
;
352 bool XMLCharRfcLanguageTagHdl::exportXML( OUString
& rStrExpValue
, const uno::Any
& rValue
, const SvXMLUnitConverter
& ) const
354 lang::Locale aLocale
;
355 if(!(rValue
>>= aLocale
))
358 // Do not write rfc-language-tag='none' if BCP 47 is not needed.
359 if (aLocale
.Variant
.isEmpty())
362 if (SvtSaveOptions().GetODFDefaultVersion() < SvtSaveOptions::ODFVER_012
)
365 rStrExpValue
= aLocale
.Variant
;
370 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */