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 "cellvalueconversion.hxx" 21 22 #include <com/sun/star/util/NumberFormatter.hpp> 23 #include <com/sun/star/util/NumberFormatsSupplier.hpp> 24 #include <com/sun/star/beans/XPropertySet.hpp> 25 #include <com/sun/star/util/Date.hpp> 26 #include <com/sun/star/util/Time.hpp> 27 #include <com/sun/star/util/DateTime.hpp> 28 #include <com/sun/star/util/XNumberFormatTypes.hpp> 29 #include <com/sun/star/util/NumberFormat.hpp> 30 #include <rtl/math.hxx> 31 #include <sal/log.hxx> 32 #include <tools/date.hxx> 33 #include <tools/time.hxx> 34 #include <tools/diagnose_ex.h> 35 #include <tools/long.hxx> 36 #include <unotools/syslocale.hxx> 37 #include <i18nlangtag/languagetag.hxx> 38 #include <comphelper/processfactory.hxx> 39 40 #include <memory> 41 #include <unordered_map> 42 43 namespace svt 44 { 45 46 47 using namespace ::com::sun::star::uno; 48 using ::com::sun::star::util::XNumberFormatter; 49 using ::com::sun::star::util::NumberFormatter; 50 using ::com::sun::star::util::XNumberFormatsSupplier; 51 using ::com::sun::star::util::NumberFormatsSupplier; 52 using ::com::sun::star::beans::XPropertySet; 53 using ::com::sun::star::lang::Locale; 54 using ::com::sun::star::util::DateTime; 55 using ::com::sun::star::util::XNumberFormatTypes; 56 57 namespace NumberFormat = ::com::sun::star::util::NumberFormat; 58 59 60 //= helper 61 62 namespace 63 { 64 lcl_convertDateToDays(sal_uInt16 const i_day,sal_uInt16 const i_month,sal_Int16 const i_year)65 double lcl_convertDateToDays( sal_uInt16 const i_day, sal_uInt16 const i_month, sal_Int16 const i_year ) 66 { 67 tools::Long const nNullDateDays = ::Date::DateToDays( 1, 1, 1900 ); 68 tools::Long const nValueDateDays = ::Date::DateToDays( i_day, i_month, i_year ); 69 70 return nValueDateDays - nNullDateDays; 71 } 72 73 lcl_convertTimeToDays(tools::Long const i_hours,tools::Long const i_minutes,tools::Long const i_seconds,tools::Long const i_100thSeconds)74 double lcl_convertTimeToDays( tools::Long const i_hours, tools::Long const i_minutes, tools::Long const i_seconds, tools::Long const i_100thSeconds ) 75 { 76 return tools::Time( i_hours, i_minutes, i_seconds, i_100thSeconds ).GetTimeInDays(); 77 } 78 79 //= CellValueConversion_Data 80 class StandardFormatNormalizer; 81 82 } 83 84 struct CellValueConversion_Data 85 { 86 typedef std::unordered_map< OUString, std::shared_ptr< StandardFormatNormalizer > > NormalizerCache; 87 88 Reference< XNumberFormatter > xNumberFormatter; 89 bool bAttemptedFormatterCreation; 90 NormalizerCache aNormalizers; 91 CellValueConversion_Datasvt::CellValueConversion_Data92 CellValueConversion_Data() 93 :xNumberFormatter() 94 ,bAttemptedFormatterCreation( false ) 95 ,aNormalizers() 96 { 97 } 98 }; 99 100 101 //= StandardFormatNormalizer 102 103 namespace { 104 105 class StandardFormatNormalizer 106 { 107 public: 108 /** converts the given <code>Any</code> into a <code>double</code> value to be fed into a number formatter 109 */ 110 virtual double convertToDouble( Any const & i_value ) const = 0; 111 112 /** returns the format key to be used for formatting values 113 */ getFormatKey() const114 sal_Int32 getFormatKey() const 115 { 116 return m_nFormatKey; 117 } 118 119 protected: StandardFormatNormalizer(Reference<XNumberFormatter> const & i_formatter,::sal_Int32 const i_numberFormatType)120 StandardFormatNormalizer( Reference< XNumberFormatter > const & i_formatter, ::sal_Int32 const i_numberFormatType ) 121 :m_nFormatKey( 0 ) 122 { 123 try 124 { 125 ENSURE_OR_THROW( i_formatter.is(), "StandardFormatNormalizer: no formatter!" ); 126 Reference< XNumberFormatsSupplier > const xSupplier( i_formatter->getNumberFormatsSupplier(), UNO_SET_THROW ); 127 Reference< XNumberFormatTypes > const xTypes( xSupplier->getNumberFormats(), UNO_QUERY_THROW ); 128 m_nFormatKey = xTypes->getStandardFormat( i_numberFormatType, SvtSysLocale().GetLanguageTag().getLocale() ); 129 } 130 catch( const Exception& ) 131 { 132 DBG_UNHANDLED_EXCEPTION("svtools.table"); 133 } 134 } 135 ~StandardFormatNormalizer()136 virtual ~StandardFormatNormalizer() {} 137 138 private: 139 ::sal_Int32 m_nFormatKey; 140 }; 141 142 143 //= DoubleNormalization 144 145 class DoubleNormalization : public StandardFormatNormalizer 146 { 147 public: DoubleNormalization(Reference<XNumberFormatter> const & i_formatter)148 explicit DoubleNormalization( Reference< XNumberFormatter > const & i_formatter ) 149 :StandardFormatNormalizer( i_formatter, NumberFormat::NUMBER ) 150 { 151 } 152 convertToDouble(Any const & i_value) const153 virtual double convertToDouble( Any const & i_value ) const override 154 { 155 double returnValue(0); 156 ::rtl::math::setNan( &returnValue ); 157 OSL_VERIFY( i_value >>= returnValue ); 158 return returnValue; 159 } 160 }; 161 162 163 //= IntegerNormalization 164 165 class IntegerNormalization : public StandardFormatNormalizer 166 { 167 public: IntegerNormalization(Reference<XNumberFormatter> const & i_formatter)168 explicit IntegerNormalization( Reference< XNumberFormatter > const & i_formatter ) 169 :StandardFormatNormalizer( i_formatter, NumberFormat::NUMBER ) 170 { 171 } 172 convertToDouble(Any const & i_value) const173 virtual double convertToDouble( Any const & i_value ) const override 174 { 175 sal_Int64 value( 0 ); 176 OSL_VERIFY( i_value >>= value ); 177 return value; 178 } 179 }; 180 181 182 //= BooleanNormalization 183 184 class BooleanNormalization : public StandardFormatNormalizer 185 { 186 public: BooleanNormalization(Reference<XNumberFormatter> const & i_formatter)187 explicit BooleanNormalization( Reference< XNumberFormatter > const & i_formatter ) 188 :StandardFormatNormalizer( i_formatter, NumberFormat::LOGICAL ) 189 { 190 } 191 convertToDouble(Any const & i_value) const192 virtual double convertToDouble( Any const & i_value ) const override 193 { 194 bool value( false ); 195 OSL_VERIFY( i_value >>= value ); 196 return value ? 1 : 0; 197 } 198 }; 199 200 201 //= DateTimeNormalization 202 203 class DateTimeNormalization : public StandardFormatNormalizer 204 { 205 public: DateTimeNormalization(Reference<XNumberFormatter> const & i_formatter)206 explicit DateTimeNormalization( Reference< XNumberFormatter > const & i_formatter ) 207 :StandardFormatNormalizer( i_formatter, NumberFormat::DATETIME ) 208 { 209 } 210 convertToDouble(Any const & i_value) const211 virtual double convertToDouble( Any const & i_value ) const override 212 { 213 double returnValue(0); 214 ::rtl::math::setNan( &returnValue ); 215 216 // extract actual UNO value 217 DateTime aDateTimeValue; 218 ENSURE_OR_RETURN( i_value >>= aDateTimeValue, "allowed for DateTime values only", returnValue ); 219 220 // date part 221 returnValue = lcl_convertDateToDays( aDateTimeValue.Day, aDateTimeValue.Month, aDateTimeValue.Year ); 222 223 // time part 224 returnValue += lcl_convertTimeToDays( 225 aDateTimeValue.Hours, aDateTimeValue.Minutes, aDateTimeValue.Seconds, aDateTimeValue.NanoSeconds ); 226 227 // done 228 return returnValue; 229 } 230 }; 231 232 233 //= DateNormalization 234 235 class DateNormalization : public StandardFormatNormalizer 236 { 237 public: DateNormalization(Reference<XNumberFormatter> const & i_formatter)238 explicit DateNormalization( Reference< XNumberFormatter > const & i_formatter ) 239 :StandardFormatNormalizer( i_formatter, NumberFormat::DATE ) 240 { 241 } 242 convertToDouble(Any const & i_value) const243 virtual double convertToDouble( Any const & i_value ) const override 244 { 245 double returnValue(0); 246 ::rtl::math::setNan( &returnValue ); 247 248 // extract 249 css::util::Date aDateValue; 250 ENSURE_OR_RETURN( i_value >>= aDateValue, "allowed for Date values only", returnValue ); 251 252 // convert 253 returnValue = lcl_convertDateToDays( aDateValue.Day, aDateValue.Month, aDateValue.Year ); 254 255 // done 256 return returnValue; 257 } 258 }; 259 260 261 //= TimeNormalization 262 263 class TimeNormalization : public StandardFormatNormalizer 264 { 265 public: TimeNormalization(Reference<XNumberFormatter> const & i_formatter)266 explicit TimeNormalization( Reference< XNumberFormatter > const & i_formatter ) 267 :StandardFormatNormalizer( i_formatter, NumberFormat::TIME ) 268 { 269 } 270 convertToDouble(Any const & i_value) const271 virtual double convertToDouble( Any const & i_value ) const override 272 { 273 double returnValue(0); 274 ::rtl::math::setNan( &returnValue ); 275 276 // extract 277 css::util::Time aTimeValue; 278 ENSURE_OR_RETURN( i_value >>= aTimeValue, "allowed for tools::Time values only", returnValue ); 279 280 // convert 281 returnValue += lcl_convertTimeToDays( 282 aTimeValue.Hours, aTimeValue.Minutes, aTimeValue.Seconds, aTimeValue.NanoSeconds ); 283 284 // done 285 return returnValue; 286 } 287 }; 288 289 290 //= operations 291 lcl_ensureNumberFormatter(CellValueConversion_Data & io_data)292 bool lcl_ensureNumberFormatter( CellValueConversion_Data & io_data ) 293 { 294 if ( io_data.bAttemptedFormatterCreation ) 295 return io_data.xNumberFormatter.is(); 296 io_data.bAttemptedFormatterCreation = true; 297 298 try 299 { 300 Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); 301 // a number formatter 302 Reference< XNumberFormatter > const xFormatter( NumberFormatter::create( xContext ), UNO_QUERY_THROW ); 303 304 // a supplier of number formats 305 Locale aLocale = SvtSysLocale().GetLanguageTag().getLocale(); 306 307 Reference< XNumberFormatsSupplier > const xSupplier = 308 NumberFormatsSupplier::createWithLocale( xContext, aLocale ); 309 310 // ensure a NullDate we will assume later on 311 css::util::Date const aNullDate( 1, 1, 1900 ); 312 Reference< XPropertySet > const xFormatSettings( xSupplier->getNumberFormatSettings(), UNO_SET_THROW ); 313 xFormatSettings->setPropertyValue( "NullDate", makeAny( aNullDate ) ); 314 315 // knit 316 xFormatter->attachNumberFormatsSupplier( xSupplier ); 317 318 // done 319 io_data.xNumberFormatter = xFormatter; 320 } 321 catch( const Exception& ) 322 { 323 DBG_UNHANDLED_EXCEPTION("svtools.table"); 324 } 325 326 return io_data.xNumberFormatter.is(); 327 } 328 329 lcl_getValueNormalizer(CellValueConversion_Data & io_data,Type const & i_valueType,std::shared_ptr<StandardFormatNormalizer> & o_formatter)330 bool lcl_getValueNormalizer( CellValueConversion_Data & io_data, Type const & i_valueType, 331 std::shared_ptr< StandardFormatNormalizer > & o_formatter ) 332 { 333 CellValueConversion_Data::NormalizerCache::const_iterator pos = io_data.aNormalizers.find( i_valueType.getTypeName() ); 334 if ( pos == io_data.aNormalizers.end() ) 335 { 336 // never encountered this type before 337 o_formatter.reset(); 338 339 OUString const sTypeName( i_valueType.getTypeName() ); 340 TypeClass const eTypeClass = i_valueType.getTypeClass(); 341 342 if ( sTypeName == ::cppu::UnoType< DateTime >::get().getTypeName() ) 343 { 344 o_formatter = std::make_shared<DateTimeNormalization>( io_data.xNumberFormatter ); 345 } 346 else if ( sTypeName == ::cppu::UnoType< css::util::Date >::get().getTypeName() ) 347 { 348 o_formatter = std::make_shared<DateNormalization>( io_data.xNumberFormatter ); 349 } 350 else if ( sTypeName == ::cppu::UnoType< css::util::Time >::get().getTypeName() ) 351 { 352 o_formatter = std::make_shared<TimeNormalization>( io_data.xNumberFormatter ); 353 } 354 else if ( sTypeName == ::cppu::UnoType< sal_Bool >::get().getTypeName() ) 355 { 356 o_formatter = std::make_shared<BooleanNormalization>( io_data.xNumberFormatter ); 357 } 358 else if ( sTypeName == ::cppu::UnoType< double >::get().getTypeName() 359 || sTypeName == ::cppu::UnoType< float >::get().getTypeName() 360 ) 361 { 362 o_formatter = std::make_shared<DoubleNormalization>( io_data.xNumberFormatter ); 363 } 364 else if ( ( eTypeClass == TypeClass_BYTE ) 365 || ( eTypeClass == TypeClass_SHORT ) 366 || ( eTypeClass == TypeClass_UNSIGNED_SHORT ) 367 || ( eTypeClass == TypeClass_LONG ) 368 || ( eTypeClass == TypeClass_UNSIGNED_LONG ) 369 || ( eTypeClass == TypeClass_HYPER ) 370 ) 371 { 372 o_formatter = std::make_shared<IntegerNormalization>( io_data.xNumberFormatter ); 373 } 374 else 375 { 376 SAL_WARN( "svtools.table", "unsupported type '" << sTypeName << "'!" ); 377 } 378 io_data.aNormalizers[ sTypeName ] = o_formatter; 379 } 380 else 381 o_formatter = pos->second; 382 383 return bool(o_formatter); 384 } 385 } 386 387 388 //= CellValueConversion 389 390 CellValueConversion()391 CellValueConversion::CellValueConversion() 392 :m_pData( new CellValueConversion_Data ) 393 { 394 } 395 396 ~CellValueConversion()397 CellValueConversion::~CellValueConversion() 398 { 399 } 400 401 convertToString(const Any & i_value)402 OUString CellValueConversion::convertToString( const Any& i_value ) 403 { 404 OUString sStringValue; 405 if ( !i_value.hasValue() ) 406 return sStringValue; 407 408 if ( ! ( i_value >>= sStringValue ) ) 409 { 410 if ( lcl_ensureNumberFormatter( *m_pData ) ) 411 { 412 std::shared_ptr< StandardFormatNormalizer > pNormalizer; 413 if ( lcl_getValueNormalizer( *m_pData, i_value.getValueType(), pNormalizer ) ) 414 { 415 try 416 { 417 double const formatterCompliantValue = pNormalizer->convertToDouble( i_value ); 418 sal_Int32 const formatKey = pNormalizer->getFormatKey(); 419 sStringValue = m_pData->xNumberFormatter->convertNumberToString( 420 formatKey, formatterCompliantValue ); 421 } 422 catch( const Exception& ) 423 { 424 DBG_UNHANDLED_EXCEPTION("svtools.table"); 425 } 426 } 427 } 428 } 429 430 return sStringValue; 431 } 432 433 434 } // namespace svt 435 436 437 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 438