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