tdf#154285 Check upper bound of arguments in SbRtl_Minute function
[LibreOffice.git] / xmloff / source / style / chrlohdl.cxx
blob05dd6d770e5a2af8dd3356b352f544e6702b539d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <sal/log.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 // 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.
43 XMLCharLanguageHdl::~XMLCharLanguageHdl()
45 // nothing to do
48 bool XMLCharLanguageHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
50 bool bRet = false;
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 );
59 else
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 );
74 return bRet;
77 bool XMLCharLanguageHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
79 lang::Locale aLocale;
80 rValue >>= aLocale;
82 if( !IsXMLToken(rStrImpValue, XML_NONE) )
84 if (aLocale.Variant.isEmpty())
85 aLocale.Language = rStrImpValue;
86 else
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");
93 else
95 aLocale.Variant = rStrImpValue + aLocale.Variant;
96 if (!aLocale.Country.isEmpty())
97 aLocale.Variant += "-" + aLocale.Country;
98 aLocale.Language = I18NLANGTAG_QLT;
103 rValue <<= aLocale;
104 return true;
107 bool XMLCharLanguageHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
109 lang::Locale aLocale;
110 if(!(rValue >>= aLocale))
111 return false;
113 if (aLocale.Variant.isEmpty())
114 rStrExpValue = aLocale.Language;
115 else
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;
127 if( rStrExpValue.isEmpty() )
128 rStrExpValue = GetXMLToken( XML_NONE );
130 return true;
133 XMLCharScriptHdl::~XMLCharScriptHdl()
135 // nothing to do
138 bool XMLCharScriptHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
140 bool bRet = false;
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)
148 bRet = true;
149 else if (bEmptyVariant1 != bEmptyVariant2)
150 ; // stays false
151 else
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 );
166 return bRet;
169 bool XMLCharScriptHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
171 lang::Locale aLocale;
172 rValue >>= aLocale;
174 if( !IsXMLToken( rStrImpValue, XML_NONE ) )
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())
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;
187 else
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);
200 else
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())
210 SAL_WARN( "xmloff.style", "XMLCharScriptHdl::importXML - attempt to insert script over bcp47: "
211 << rStrImpValue << " -> " << aLanguageTag.getBcp47());
213 #endif
217 rValue <<= aLocale;
218 return true;
221 bool XMLCharScriptHdl::exportXML(OUString& rStrExpValue,
222 const uno::Any& rValue, const SvXMLUnitConverter& rUnitConv) const
224 lang::Locale aLocale;
225 if(!(rValue >>= aLocale))
226 return false;
228 // Do not write script='none' for default script.
230 if (aLocale.Variant.isEmpty())
231 return false;
233 LanguageTag aLanguageTag( aLocale);
234 if (!aLanguageTag.hasScript())
235 return false;
237 if (rUnitConv.getSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012)
238 return false;
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();
248 XMLCharCountryHdl::~XMLCharCountryHdl()
250 // nothing to do
253 bool XMLCharCountryHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
255 bool bRet = false;
256 lang::Locale aLocale1, aLocale2;
258 if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
259 bRet = ( aLocale1.Country == aLocale2.Country );
261 return bRet;
264 bool XMLCharCountryHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
266 lang::Locale aLocale;
267 rValue >>= aLocale;
269 if( !IsXMLToken( rStrImpValue, XML_NONE ) )
271 if (aLocale.Country.isEmpty())
273 aLocale.Country = rStrImpValue;
274 if (aLocale.Variant.getLength() >= 7 && aLocale.Language == I18NLANGTAG_QLT)
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())
281 i = aLocale.Variant.indexOf( '-', i+1);
282 if (i < 0) // no other separator
283 aLocale.Variant += "-" + rStrImpValue; // append country
289 rValue <<= aLocale;
290 return true;
293 bool XMLCharCountryHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
295 lang::Locale aLocale;
296 if(!(rValue >>= aLocale))
297 return false;
299 if (aLocale.Variant.isEmpty())
300 rStrExpValue = aLocale.Country;
301 else
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;
313 if( rStrExpValue.isEmpty() )
314 rStrExpValue = GetXMLToken( XML_NONE );
316 return true;
319 XMLCharRfcLanguageTagHdl::~XMLCharRfcLanguageTagHdl()
321 // nothing to do
324 bool XMLCharRfcLanguageTagHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
326 bool bRet = false;
327 lang::Locale aLocale1, aLocale2;
329 if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
330 bRet = ( aLocale1.Variant == aLocale2.Variant );
332 return bRet;
335 bool XMLCharRfcLanguageTagHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
337 lang::Locale aLocale;
338 rValue >>= aLocale;
340 if( !IsXMLToken( rStrImpValue, XML_NONE ) )
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)
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)
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))
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)
378 // No match, so we still need the qlt anyway. Whatever..
379 aLocale.Variant = rStrImpValue;
380 aLocale.Language = I18NLANGTAG_QLT;
383 else if (aLang.isEmpty() && aCoun.isEmpty())
385 // Both empty, some other tag.
386 aLocale.Variant = rStrImpValue;
387 aLocale.Language = I18NLANGTAG_QLT;
390 SAL_WARN("xmloff.style","rfc-language-tag too short: now set: "
391 << aLocale.Language <<","<< aLocale.Country <<","<< aLocale.Variant);
393 else
395 aLocale.Variant = rStrImpValue;
396 aLocale.Language = I18NLANGTAG_QLT;
400 rValue <<= aLocale;
401 return true;
404 bool XMLCharRfcLanguageTagHdl::exportXML(OUString& rStrExpValue,
405 const uno::Any& rValue, const SvXMLUnitConverter& rUnitConv) const
407 lang::Locale aLocale;
408 if(!(rValue >>= aLocale))
409 return false;
411 // Do not write rfc-language-tag='none' if BCP 47 is not needed.
412 if (aLocale.Variant.isEmpty())
413 return false;
415 if (rUnitConv.getSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012)
416 return false;
418 rStrExpValue = aLocale.Variant;
420 return true;
423 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */