1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <connectivity/dbconversion.hxx>
21 #include <connectivity/dbtools.hxx>
22 #include <com/sun/star/script/XTypeConverter.hpp>
23 #include <com/sun/star/sdbc/DataType.hpp>
24 #include <com/sun/star/util/NumberFormat.hpp>
25 #include <com/sun/star/util/XNumberFormatter.hpp>
26 #include <com/sun/star/util/XNumberFormatTypes.hpp>
27 #include <com/sun/star/sdb/XColumnUpdate.hpp>
28 #include <com/sun/star/sdb/XColumn.hpp>
29 #include <com/sun/star/beans/XPropertySet.hpp>
30 #include <comphelper/extract.hxx>
31 #include <TConnection.hxx>
32 #include <comphelper/numbers.hxx>
33 #include <comphelper/types.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <sal/log.hxx>
36 #include <comphelper/diagnose_ex.hxx>
39 using namespace ::connectivity
;
40 using namespace ::comphelper
;
41 using namespace ::com::sun::star::script
;
42 using namespace ::com::sun::star::sdb
;
43 using namespace ::com::sun::star::sdbc
;
44 using namespace ::dbtools
;
45 using namespace ::com::sun::star::lang
;
46 using namespace ::com::sun::star::beans
;
47 using namespace ::com::sun::star::util
;
48 using namespace ::com::sun::star::uno
;
50 OUString
DBTypeConversion::toSQLString(sal_Int32 eType
, const Any
& _rVal
,
51 const Reference
< XTypeConverter
>& _rxTypeConverter
)
60 case DataType::INTEGER
:
62 case DataType::BOOLEAN
:
63 case DataType::TINYINT
:
64 case DataType::SMALLINT
:
65 if (_rVal
.getValueTypeClass() == css::uno::TypeClass_BOOLEAN
)
67 if (::cppu::any2bool(_rVal
))
75 _rxTypeConverter
->convertToSimpleType(_rVal
, TypeClass_STRING
) >>= sTemp
;
80 case DataType::VARCHAR
:
81 case DataType::LONGVARCHAR
:
85 _rxTypeConverter
->convertToSimpleType(_rVal
, TypeClass_STRING
) >>= aTemp
;
86 aTemp
= aTemp
.replaceAll(u
"\'", u
"\'\'");
92 case DataType::DOUBLE
:
93 case DataType::DECIMAL
:
94 case DataType::NUMERIC
:
95 case DataType::BIGINT
:
99 _rxTypeConverter
->convertToSimpleType(_rVal
, TypeClass_STRING
) >>= sTemp
;
103 case DataType::TIMESTAMP
:
107 if (_rVal
.getValueTypeClass() == css::uno::TypeClass_DOUBLE
)
111 aDateTime
= DBTypeConversion::toDateTime(nValue
);
114 else if (_rVal
.getValueTypeClass() == css::uno::TypeClass_STRING
)
118 aDateTime
= DBTypeConversion::toDateTime(sValue
);
122 bOk
= _rVal
>>= aDateTime
;
124 OSL_ENSURE( bOk
, "DBTypeConversion::toSQLString: _rVal is not datetime!");
125 // check if this is really a timestamp or only a date
129 + DBTypeConversion::toDateTimeString(aDateTime
)
139 if (_rVal
.getValueTypeClass() == css::uno::TypeClass_DOUBLE
)
143 aDate
= DBTypeConversion::toDate(nValue
);
146 else if (_rVal
.getValueTypeClass() == css::uno::TypeClass_STRING
)
150 aDate
= DBTypeConversion::toDate(sValue
);
154 bOk
= _rVal
>>= aDate
;
155 OSL_ENSURE( bOk
, "DBTypeConversion::toSQLString: _rVal is not date!");
157 + DBTypeConversion::toDateString(aDate
)
162 css::util::Time aTime
;
164 if (_rVal
.getValueTypeClass() == css::uno::TypeClass_DOUBLE
)
168 aTime
= DBTypeConversion::toTime(nValue
);
171 else if (_rVal
.getValueTypeClass() == css::uno::TypeClass_STRING
)
175 aTime
= DBTypeConversion::toTime(sValue
);
179 bOk
= _rVal
>>= aTime
;
180 OSL_ENSURE( bOk
,"DBTypeConversion::toSQLString: _rVal is not time!");
182 + DBTypeConversion::toTimeString(aTime
)
187 catch ( const Exception
& )
189 OSL_FAIL("TypeConversion Error");
193 aRet
.append(" NULL ");
194 return aRet
.makeStringAndClear();
197 Date
DBTypeConversion::getNULLDate(const Reference
< XNumberFormatsSupplier
> &xSupplier
)
199 OSL_ENSURE(xSupplier
.is(), "getNULLDate : the formatter doesn't implement a supplier !");
206 xSupplier
->getNumberFormatSettings()->getPropertyValue(u
"NullDate"_ustr
) >>= aDate
;
209 catch ( const Exception
& )
214 return getStandardDate();
217 void DBTypeConversion::setValue(const Reference
<XColumnUpdate
>& xVariant
,
218 const Reference
<XNumberFormatter
>& xFormatter
,
219 const Date
& rNullDate
,
220 const OUString
& rString
,
222 sal_Int16 nFieldType
,
225 if (!rString
.isEmpty())
227 // Does the String need to be formatted?
228 sal_Int16 nTypeClass
= nKeyType
& ~NumberFormat::DEFINED
;
229 bool bTextFormat
= nTypeClass
== NumberFormat::TEXT
;
230 sal_Int32 nKeyToUse
= bTextFormat
? 0 : nKey
;
231 sal_Int16 nRealUsedTypeClass
= nTypeClass
;
232 // for a Text-Format the formatter needs some more freedom, otherwise
233 // convertStringToNumber will throw a NotNumericException
236 double fValue
= xFormatter
->convertStringToNumber(nKeyToUse
, rString
);
237 Reference
< XNumberFormats
> xFormats(xFormatter
->getNumberFormatsSupplier()->getNumberFormats());
238 Reference
< XNumberFormatTypes
> xFormatTypes(xFormats
, UNO_QUERY
);
239 sal_Int32
nStandardKey(0);
240 if(xFormatTypes
.is())
242 const Reference
< XPropertySet
> xFormatProps(xFormats
->getByKey(nKeyToUse
));
243 if (xFormatProps
.is())
245 css::lang::Locale loc
;
246 if (xFormatProps
->getPropertyValue(u
"Locale"_ustr
) >>= loc
)
247 nStandardKey
= xFormatTypes
->getStandardIndex(loc
);
255 SAL_WARN("connectivity.commontools", "no format by key " << nKeyToUse
);
262 // Why use nStandardKey rather than nKeyToUse here? I'm not sure, but "it was always like that".
263 // Previously had hardcoded 0 instead of nStandardKey, which led to problems with dates
264 // because of differences M/D/Y vs D/M/Y. This at least fixes those problems, but possibly
265 // nKeyToUse is an even better choice than nStandardKey.
266 // OTOH, using nKeyToUse nullifies the special treatment for percent formats,
267 // leading to "5" (in a percent format) to be understood as "500%" instead of "5%".
268 sal_Int32 nRealUsedKey
= xFormatter
->detectNumberFormat(nStandardKey
, rString
);
269 if (nRealUsedKey
!= nKeyToUse
)
270 nRealUsedTypeClass
= getNumberFormatType(xFormatter
, nRealUsedKey
) & ~NumberFormat::DEFINED
;
272 // and again a special treatment, this time for percent formats
273 if ((NumberFormat::NUMBER
== nRealUsedTypeClass
) && (NumberFormat::PERCENT
== nTypeClass
))
274 { // formatting should be "percent", but the String provides just a simple number -> adjust
275 OUString sExpanded
= rString
+ "%";
276 fValue
= xFormatter
->convertStringToNumber(nKeyToUse
, sExpanded
);
279 switch (nRealUsedTypeClass
)
281 case NumberFormat::DATE
:
282 case NumberFormat::DATETIME
:
283 case NumberFormat::TIME
:
284 DBTypeConversion::setValue(xVariant
,rNullDate
,fValue
,nRealUsedTypeClass
);
286 case NumberFormat::CURRENCY
:
287 case NumberFormat::NUMBER
:
288 case NumberFormat::SCIENTIFIC
:
289 case NumberFormat::FRACTION
:
290 case NumberFormat::PERCENT
:
291 xVariant
->updateDouble(fValue
);
294 xVariant
->updateString(rString
);
297 catch(const Exception
& )
299 xVariant
->updateString(rString
);
306 case css::sdbc::DataType::CHAR
:
307 case css::sdbc::DataType::VARCHAR
:
308 case css::sdbc::DataType::LONGVARCHAR
:
309 xVariant
->updateString(rString
);
312 xVariant
->updateNull();
318 void DBTypeConversion::setValue(const Reference
<XColumnUpdate
>& xVariant
,
319 const Date
& rNullDate
,
320 const double& rValue
,
323 switch (nKeyType
& ~NumberFormat::DEFINED
)
325 case NumberFormat::DATE
:
326 xVariant
->updateDate(toDate( rValue
, rNullDate
));
328 case NumberFormat::DATETIME
:
329 xVariant
->updateTimestamp(toDateTime(rValue
,rNullDate
));
331 case NumberFormat::TIME
:
332 xVariant
->updateTime(toTime(rValue
));
336 double nValue
= rValue
;
337 // Reference<XPropertySet> xProp(xVariant,UNO_QUERY);
339 // && xProp->getPropertySetInfo()->hasPropertyByName(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISSIGNED))
340 // && !::comphelper::getBOOL(xProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISSIGNED))) )
342 // switch (::comphelper::getINT32(xProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))))
344 // case DataType::TINYINT:
345 // nValue = static_cast<sal_uInt8>(rValue);
347 // case DataType::SMALLINT:
348 // nValue = static_cast<sal_uInt16>(rValue);
350 // case DataType::INTEGER:
351 // nValue = static_cast<sal_uInt32>(rValue);
353 // case DataType::BIGINT:
354 // nValue = static_cast<sal_uInt64>(rValue);
358 xVariant
->updateDouble(nValue
);
364 double DBTypeConversion::getValue( const Reference
< XColumn
>& i_column
, const Date
& i_relativeToNullDate
)
368 const Reference
< XPropertySet
> xProp( i_column
, UNO_QUERY_THROW
);
370 const sal_Int32 nColumnType
= ::comphelper::getINT32( xProp
->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_TYPE
) ) );
371 switch ( nColumnType
)
374 return toDouble( i_column
->getDate(), i_relativeToNullDate
);
377 return toDouble( i_column
->getTime() );
379 case DataType::TIMESTAMP
:
380 return toDouble( i_column
->getTimestamp(), i_relativeToNullDate
);
384 bool bIsSigned
= true;
385 OSL_VERIFY( xProp
->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_ISSIGNED
) ) >>= bIsSigned
);
388 switch ( nColumnType
)
390 case DataType::TINYINT
:
391 return static_cast<double>(static_cast<sal_uInt8
>(i_column
->getByte()));
392 case DataType::SMALLINT
:
393 return static_cast<double>(static_cast<sal_uInt16
>(i_column
->getShort()));
394 case DataType::INTEGER
:
395 return static_cast<double>(static_cast<sal_uInt32
>(i_column
->getInt()));
396 case DataType::BIGINT
:
397 return static_cast<double>(static_cast<sal_uInt64
>(i_column
->getLong()));
401 return i_column
->getDouble();
404 catch( const Exception
& )
406 DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
411 OUString
DBTypeConversion::getFormattedValue(const Reference
< XPropertySet
>& _xColumn
,
412 const Reference
<XNumberFormatter
>& _xFormatter
,
413 const css::lang::Locale
& _rLocale
,
414 const Date
& _rNullDate
)
416 OSL_ENSURE(_xColumn
.is() && _xFormatter
.is(), "DBTypeConversion::getFormattedValue: invalid arg !");
417 if (!_xColumn
.is() || !_xFormatter
.is())
423 _xColumn
->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY
)) >>= nKey
;
425 catch (const Exception
& )
427 TOOLS_WARN_EXCEPTION( "connectivity.commontools", "DBTypeConversion::getValue: caught an exception while asking for the format key!");
432 Reference
<XNumberFormats
> xFormats( _xFormatter
->getNumberFormatsSupplier()->getNumberFormats() );
434 nKey
= ::dbtools::getDefaultNumberFormat(_xColumn
,
435 Reference
< XNumberFormatTypes
> (xFormats
, UNO_QUERY
),
440 sal_Int16 nKeyType
= getNumberFormatType(_xFormatter
, nKey
) & ~NumberFormat::DEFINED
;
442 return DBTypeConversion::getFormattedValue(Reference
< XColumn
> (_xColumn
, UNO_QUERY
), _xFormatter
, _rNullDate
, nKey
, nKeyType
);
446 OUString
DBTypeConversion::getFormattedValue(const Reference
<XColumn
>& xVariant
,
447 const Reference
<XNumberFormatter
>& xFormatter
,
448 const Date
& rNullDate
,
457 switch (nKeyType
& ~NumberFormat::DEFINED
)
459 case NumberFormat::DATE
:
460 case NumberFormat::DATETIME
:
462 // get a value which represents the given date, relative to the given null date
463 double fValue
= getValue( xVariant
, rNullDate
);
464 if ( !xVariant
->wasNull() )
466 // get the null date of the formatter
467 Date
aFormatterNullDate( rNullDate
);
470 Reference
< XNumberFormatsSupplier
> xSupplier( xFormatter
->getNumberFormatsSupplier(), UNO_SET_THROW
);
471 Reference
< XPropertySet
> xFormatterSettings( xSupplier
->getNumberFormatSettings(), UNO_SET_THROW
);
472 OSL_VERIFY( xFormatterSettings
->getPropertyValue(u
"NullDate"_ustr
) >>= aFormatterNullDate
);
474 catch( const Exception
& )
476 DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
478 // get a value which represents the given date, relative to the null date of the formatter
479 fValue
-= toDays( rNullDate
, aFormatterNullDate
);
481 aString
= xFormatter
->convertNumberToString( nKey
, fValue
);
485 case NumberFormat::TIME
:
486 case NumberFormat::NUMBER
:
487 case NumberFormat::SCIENTIFIC
:
488 case NumberFormat::FRACTION
:
489 case NumberFormat::PERCENT
:
491 double fValue
= xVariant
->getDouble();
492 if (!xVariant
->wasNull())
493 aString
= xFormatter
->convertNumberToString(nKey
, fValue
);
495 case NumberFormat::CURRENCY
:
497 double fValue
= xVariant
->getDouble();
498 if (!xVariant
->wasNull())
499 aString
= xFormatter
->getInputString(nKey
, fValue
);
501 case NumberFormat::TEXT
:
502 aString
= xFormatter
->formatString(nKey
, xVariant
->getString());
505 aString
= xVariant
->getString();
508 catch(const Exception
& )
510 aString
= xVariant
->getString();
517 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */