Files
libreoffice/svtools/source/table/cellvalueconversion.cxx
Stephan Bergmann 7ca8ff7ff7 loplugin:unnecessaryoverride (dtors) in svtools
Change-Id: I5b0f4b335cd383702324ff6ea04581476309ca70
2016-12-02 11:15:15 +01:00

436 lines
14 KiB
C++

/* -*- 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/strbuf.hxx>
#include <rtl/math.hxx>
#include <tools/date.hxx>
#include <tools/time.hxx>
#include <tools/diagnose_ex.h>
#include <unotools/syslocale.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( long const i_day, long const i_month, long 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 >, OUStringHash > NormalizerCache;
Reference< XNumberFormatter > xNumberFormatter;
bool bAttemptedFormatterCreation;
NormalizerCache aNormalizers;
CellValueConversion_Data()
:xNumberFormatter()
,bAttemptedFormatterCreation( false )
,aNormalizers()
{
}
};
//= StandardFormatNormalizer
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();
}
}
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
namespace
{
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();
}
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.equals( ::cppu::UnoType< DateTime >::get().getTypeName() ) )
{
o_formatter.reset( new DateTimeNormalization( io_data.xNumberFormatter ) );
}
else if ( sTypeName.equals( ::cppu::UnoType< css::util::Date >::get().getTypeName() ) )
{
o_formatter.reset( new DateNormalization( io_data.xNumberFormatter ) );
}
else if ( sTypeName.equals( ::cppu::UnoType< css::util::Time >::get().getTypeName() ) )
{
o_formatter.reset( new TimeNormalization( io_data.xNumberFormatter ) );
}
else if ( sTypeName.equals( ::cppu::UnoType< sal_Bool >::get().getTypeName() ) )
{
o_formatter.reset( new BooleanNormalization( io_data.xNumberFormatter ) );
}
else if ( sTypeName.equals( ::cppu::UnoType< double >::get().getTypeName() )
|| sTypeName.equals( ::cppu::UnoType< float >::get().getTypeName() )
)
{
o_formatter.reset( new 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.reset( new IntegerNormalization( io_data.xNumberFormatter ) );
}
else
{
SAL_WARN( "svtools.table", "unsupported type '" << sTypeName << "'!" );
}
io_data.aNormalizers[ sTypeName ] = o_formatter;
}
else
o_formatter = pos->second;
return !!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();
}
}
}
}
return sStringValue;
}
} // namespace svt
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */