Coverage Report

Created: 2025-12-31 10:39

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