diff options
Diffstat (limited to 'svtools/source/table/cellvalueconversion.cxx')
-rw-r--r-- | svtools/source/table/cellvalueconversion.cxx | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/svtools/source/table/cellvalueconversion.cxx b/svtools/source/table/cellvalueconversion.cxx new file mode 100644 index 000000000..2ca15c7ca --- /dev/null +++ b/svtools/source/table/cellvalueconversion.cxx @@ -0,0 +1,436 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "cellvalueconversion.hxx" + +#include <com/sun/star/util/NumberFormatter.hpp> +#include <com/sun/star/util/NumberFormatsSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <rtl/math.hxx> +#include <sal/log.hxx> +#include <tools/date.hxx> +#include <tools/time.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/syslocale.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <comphelper/processfactory.hxx> + +#include <memory> +#include <unordered_map> + +namespace svt +{ + + + using namespace ::com::sun::star::uno; + using ::com::sun::star::util::XNumberFormatter; + using ::com::sun::star::util::NumberFormatter; + using ::com::sun::star::util::XNumberFormatsSupplier; + using ::com::sun::star::util::NumberFormatsSupplier; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::lang::Locale; + using ::com::sun::star::util::DateTime; + using ::com::sun::star::util::XNumberFormatTypes; + + namespace NumberFormat = ::com::sun::star::util::NumberFormat; + + + //= helper + + namespace + { + + double lcl_convertDateToDays( sal_uInt16 const i_day, sal_uInt16 const i_month, sal_Int16 const i_year ) + { + long const nNullDateDays = ::Date::DateToDays( 1, 1, 1900 ); + long const nValueDateDays = ::Date::DateToDays( i_day, i_month, i_year ); + + return nValueDateDays - nNullDateDays; + } + + + double lcl_convertTimeToDays( long const i_hours, long const i_minutes, long const i_seconds, long const i_100thSeconds ) + { + return tools::Time( i_hours, i_minutes, i_seconds, i_100thSeconds ).GetTimeInDays(); + } + + //= CellValueConversion_Data + class StandardFormatNormalizer; + + } + + struct CellValueConversion_Data + { + typedef std::unordered_map< OUString, std::shared_ptr< StandardFormatNormalizer > > NormalizerCache; + + Reference< XNumberFormatter > xNumberFormatter; + bool bAttemptedFormatterCreation; + NormalizerCache aNormalizers; + + CellValueConversion_Data() + :xNumberFormatter() + ,bAttemptedFormatterCreation( false ) + ,aNormalizers() + { + } + }; + + + //= StandardFormatNormalizer + + namespace { + + class StandardFormatNormalizer + { + public: + /** converts the given <code>Any</code> into a <code>double</code> value to be fed into a number formatter + */ + virtual double convertToDouble( Any const & i_value ) const = 0; + + /** returns the format key to be used for formatting values + */ + sal_Int32 getFormatKey() const + { + return m_nFormatKey; + } + + protected: + StandardFormatNormalizer( Reference< XNumberFormatter > const & i_formatter, ::sal_Int32 const i_numberFormatType ) + :m_nFormatKey( 0 ) + { + try + { + ENSURE_OR_THROW( i_formatter.is(), "StandardFormatNormalizer: no formatter!" ); + Reference< XNumberFormatsSupplier > const xSupplier( i_formatter->getNumberFormatsSupplier(), UNO_SET_THROW ); + Reference< XNumberFormatTypes > const xTypes( xSupplier->getNumberFormats(), UNO_QUERY_THROW ); + m_nFormatKey = xTypes->getStandardFormat( i_numberFormatType, SvtSysLocale().GetLanguageTag().getLocale() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.table"); + } + } + + virtual ~StandardFormatNormalizer() {} + + private: + ::sal_Int32 m_nFormatKey; + }; + + + //= DoubleNormalization + + class DoubleNormalization : public StandardFormatNormalizer + { + public: + explicit DoubleNormalization( Reference< XNumberFormatter > const & i_formatter ) + :StandardFormatNormalizer( i_formatter, NumberFormat::NUMBER ) + { + } + + virtual double convertToDouble( Any const & i_value ) const override + { + double returnValue(0); + ::rtl::math::setNan( &returnValue ); + OSL_VERIFY( i_value >>= returnValue ); + return returnValue; + } + }; + + + //= IntegerNormalization + + class IntegerNormalization : public StandardFormatNormalizer + { + public: + explicit IntegerNormalization( Reference< XNumberFormatter > const & i_formatter ) + :StandardFormatNormalizer( i_formatter, NumberFormat::NUMBER ) + { + } + + virtual double convertToDouble( Any const & i_value ) const override + { + sal_Int64 value( 0 ); + OSL_VERIFY( i_value >>= value ); + return value; + } + }; + + + //= BooleanNormalization + + class BooleanNormalization : public StandardFormatNormalizer + { + public: + explicit BooleanNormalization( Reference< XNumberFormatter > const & i_formatter ) + :StandardFormatNormalizer( i_formatter, NumberFormat::LOGICAL ) + { + } + + virtual double convertToDouble( Any const & i_value ) const override + { + bool value( false ); + OSL_VERIFY( i_value >>= value ); + return value ? 1 : 0; + } + }; + + + //= DateTimeNormalization + + class DateTimeNormalization : public StandardFormatNormalizer + { + public: + explicit DateTimeNormalization( Reference< XNumberFormatter > const & i_formatter ) + :StandardFormatNormalizer( i_formatter, NumberFormat::DATETIME ) + { + } + + virtual double convertToDouble( Any const & i_value ) const override + { + double returnValue(0); + ::rtl::math::setNan( &returnValue ); + + // extract actual UNO value + DateTime aDateTimeValue; + ENSURE_OR_RETURN( i_value >>= aDateTimeValue, "allowed for DateTime values only", returnValue ); + + // date part + returnValue = lcl_convertDateToDays( aDateTimeValue.Day, aDateTimeValue.Month, aDateTimeValue.Year ); + + // time part + returnValue += lcl_convertTimeToDays( + aDateTimeValue.Hours, aDateTimeValue.Minutes, aDateTimeValue.Seconds, aDateTimeValue.NanoSeconds ); + + // done + return returnValue; + } + }; + + + //= DateNormalization + + class DateNormalization : public StandardFormatNormalizer + { + public: + explicit DateNormalization( Reference< XNumberFormatter > const & i_formatter ) + :StandardFormatNormalizer( i_formatter, NumberFormat::DATE ) + { + } + + virtual double convertToDouble( Any const & i_value ) const override + { + double returnValue(0); + ::rtl::math::setNan( &returnValue ); + + // extract + css::util::Date aDateValue; + ENSURE_OR_RETURN( i_value >>= aDateValue, "allowed for Date values only", returnValue ); + + // convert + returnValue = lcl_convertDateToDays( aDateValue.Day, aDateValue.Month, aDateValue.Year ); + + // done + return returnValue; + } + }; + + + //= TimeNormalization + + class TimeNormalization : public StandardFormatNormalizer + { + public: + explicit TimeNormalization( Reference< XNumberFormatter > const & i_formatter ) + :StandardFormatNormalizer( i_formatter, NumberFormat::TIME ) + { + } + + virtual double convertToDouble( Any const & i_value ) const override + { + double returnValue(0); + ::rtl::math::setNan( &returnValue ); + + // extract + css::util::Time aTimeValue; + ENSURE_OR_RETURN( i_value >>= aTimeValue, "allowed for tools::Time values only", returnValue ); + + // convert + returnValue += lcl_convertTimeToDays( + aTimeValue.Hours, aTimeValue.Minutes, aTimeValue.Seconds, aTimeValue.NanoSeconds ); + + // done + return returnValue; + } + }; + + + //= operations + + bool lcl_ensureNumberFormatter( CellValueConversion_Data & io_data ) + { + if ( io_data.bAttemptedFormatterCreation ) + return io_data.xNumberFormatter.is(); + io_data.bAttemptedFormatterCreation = true; + + try + { + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + // a number formatter + Reference< XNumberFormatter > const xFormatter( NumberFormatter::create( xContext ), UNO_QUERY_THROW ); + + // a supplier of number formats + Locale aLocale = SvtSysLocale().GetLanguageTag().getLocale(); + + Reference< XNumberFormatsSupplier > const xSupplier = + NumberFormatsSupplier::createWithLocale( xContext, aLocale ); + + // ensure a NullDate we will assume later on + css::util::Date const aNullDate( 1, 1, 1900 ); + Reference< XPropertySet > const xFormatSettings( xSupplier->getNumberFormatSettings(), UNO_SET_THROW ); + xFormatSettings->setPropertyValue( "NullDate", makeAny( aNullDate ) ); + + // knit + xFormatter->attachNumberFormatsSupplier( xSupplier ); + + // done + io_data.xNumberFormatter = xFormatter; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.table"); + } + + return io_data.xNumberFormatter.is(); + } + + + bool lcl_getValueNormalizer( CellValueConversion_Data & io_data, Type const & i_valueType, + std::shared_ptr< StandardFormatNormalizer > & o_formatter ) + { + CellValueConversion_Data::NormalizerCache::const_iterator pos = io_data.aNormalizers.find( i_valueType.getTypeName() ); + if ( pos == io_data.aNormalizers.end() ) + { + // never encountered this type before + o_formatter.reset(); + + OUString const sTypeName( i_valueType.getTypeName() ); + TypeClass const eTypeClass = i_valueType.getTypeClass(); + + if ( sTypeName == ::cppu::UnoType< DateTime >::get().getTypeName() ) + { + o_formatter = std::make_shared<DateTimeNormalization>( io_data.xNumberFormatter ); + } + else if ( sTypeName == ::cppu::UnoType< css::util::Date >::get().getTypeName() ) + { + o_formatter = std::make_shared<DateNormalization>( io_data.xNumberFormatter ); + } + else if ( sTypeName == ::cppu::UnoType< css::util::Time >::get().getTypeName() ) + { + o_formatter = std::make_shared<TimeNormalization>( io_data.xNumberFormatter ); + } + else if ( sTypeName == ::cppu::UnoType< sal_Bool >::get().getTypeName() ) + { + o_formatter = std::make_shared<BooleanNormalization>( io_data.xNumberFormatter ); + } + else if ( sTypeName == ::cppu::UnoType< double >::get().getTypeName() + || sTypeName == ::cppu::UnoType< float >::get().getTypeName() + ) + { + o_formatter = std::make_shared<DoubleNormalization>( io_data.xNumberFormatter ); + } + else if ( ( eTypeClass == TypeClass_BYTE ) + || ( eTypeClass == TypeClass_SHORT ) + || ( eTypeClass == TypeClass_UNSIGNED_SHORT ) + || ( eTypeClass == TypeClass_LONG ) + || ( eTypeClass == TypeClass_UNSIGNED_LONG ) + || ( eTypeClass == TypeClass_HYPER ) + ) + { + o_formatter = std::make_shared<IntegerNormalization>( io_data.xNumberFormatter ); + } + else + { + SAL_WARN( "svtools.table", "unsupported type '" << sTypeName << "'!" ); + } + io_data.aNormalizers[ sTypeName ] = o_formatter; + } + else + o_formatter = pos->second; + + return bool(o_formatter); + } + } + + + //= CellValueConversion + + + CellValueConversion::CellValueConversion() + :m_pData( new CellValueConversion_Data ) + { + } + + + CellValueConversion::~CellValueConversion() + { + } + + + OUString CellValueConversion::convertToString( const Any& i_value ) + { + OUString sStringValue; + if ( !i_value.hasValue() ) + return sStringValue; + + if ( ! ( i_value >>= sStringValue ) ) + { + if ( lcl_ensureNumberFormatter( *m_pData ) ) + { + std::shared_ptr< StandardFormatNormalizer > pNormalizer; + if ( lcl_getValueNormalizer( *m_pData, i_value.getValueType(), pNormalizer ) ) + { + try + { + double const formatterCompliantValue = pNormalizer->convertToDouble( i_value ); + sal_Int32 const formatKey = pNormalizer->getFormatKey(); + sStringValue = m_pData->xNumberFormatter->convertNumberToString( + formatKey, formatterCompliantValue ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.table"); + } + } + } + } + + return sStringValue; + } + + +} // namespace svt + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |