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 <tools/diagnose_ex.h>
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
.getValueType().getTypeClass() == 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 sal_Int32 nIndex
= sal_Int32(-2);
87 const OUString
sQuot("\'");
91 nIndex
= aTemp
.indexOf(sQuot
,nIndex
);
93 aTemp
= aTemp
.replaceAt(nIndex
,sQuot
.getLength(), "\'\'");
94 } while (nIndex
!= -1);
101 case DataType::DOUBLE
:
102 case DataType::DECIMAL
:
103 case DataType::NUMERIC
:
104 case DataType::BIGINT
:
108 _rxTypeConverter
->convertToSimpleType(_rVal
, TypeClass_STRING
) >>= sTemp
;
112 case DataType::TIMESTAMP
:
116 if (_rVal
.getValueType().getTypeClass() == css::uno::TypeClass_DOUBLE
)
120 aDateTime
= DBTypeConversion::toDateTime(nValue
);
123 else if (_rVal
.getValueType().getTypeClass() == css::uno::TypeClass_STRING
)
127 aDateTime
= DBTypeConversion::toDateTime(sValue
);
131 bOk
= _rVal
>>= aDateTime
;
133 OSL_ENSURE( bOk
, "DBTypeConversion::toSQLString: _rVal is not datetime!");
134 // check if this is really a timestamp or only a date
137 aRet
.append("{ts '");
138 aRet
.append(DBTypeConversion::toDateTimeString(aDateTime
));
148 if (_rVal
.getValueType().getTypeClass() == css::uno::TypeClass_DOUBLE
)
152 aDate
= DBTypeConversion::toDate(nValue
);
155 else if (_rVal
.getValueType().getTypeClass() == css::uno::TypeClass_STRING
)
159 aDate
= DBTypeConversion::toDate(sValue
);
163 bOk
= _rVal
>>= aDate
;
164 OSL_ENSURE( bOk
, "DBTypeConversion::toSQLString: _rVal is not date!");
166 aRet
.append(DBTypeConversion::toDateString(aDate
));
171 css::util::Time aTime
;
173 if (_rVal
.getValueType().getTypeClass() == css::uno::TypeClass_DOUBLE
)
177 aTime
= DBTypeConversion::toTime(nValue
);
180 else if (_rVal
.getValueType().getTypeClass() == css::uno::TypeClass_STRING
)
184 aTime
= DBTypeConversion::toTime(sValue
);
188 bOk
= _rVal
>>= aTime
;
189 OSL_ENSURE( bOk
,"DBTypeConversion::toSQLString: _rVal is not time!");
191 aRet
.append(DBTypeConversion::toTimeString(aTime
));
196 catch ( const Exception
& )
198 OSL_FAIL("TypeConversion Error");
202 aRet
.append(" NULL ");
203 return aRet
.makeStringAndClear();
206 Date
DBTypeConversion::getNULLDate(const Reference
< XNumberFormatsSupplier
> &xSupplier
)
208 OSL_ENSURE(xSupplier
.is(), "getNULLDate : the formatter doesn't implement a supplier !");
215 xSupplier
->getNumberFormatSettings()->getPropertyValue("NullDate") >>= aDate
;
218 catch ( const Exception
& )
223 return getStandardDate();
226 void DBTypeConversion::setValue(const Reference
<XColumnUpdate
>& xVariant
,
227 const Reference
<XNumberFormatter
>& xFormatter
,
228 const Date
& rNullDate
,
229 const OUString
& rString
,
231 sal_Int16 nFieldType
,
234 if (!rString
.isEmpty())
236 // Does the String need to be formatted?
237 sal_Int16 nTypeClass
= nKeyType
& ~NumberFormat::DEFINED
;
238 bool bTextFormat
= nTypeClass
== NumberFormat::TEXT
;
239 sal_Int32 nKeyToUse
= bTextFormat
? 0 : nKey
;
240 sal_Int16 nRealUsedTypeClass
= nTypeClass
;
241 // for a Text-Format the formatter needs some more freedom, otherwise
242 // convertStringToNumber will throw a NotNumericException
245 double fValue
= xFormatter
->convertStringToNumber(nKeyToUse
, rString
);
246 Reference
< XNumberFormats
> xFormats(xFormatter
->getNumberFormatsSupplier()->getNumberFormats());
247 Reference
< XNumberFormatTypes
> xFormatTypes(xFormats
, UNO_QUERY
);
248 sal_Int32
nStandardKey(0);
249 if(xFormatTypes
.is())
251 const Reference
< XPropertySet
> xFormatProps(xFormats
->getByKey(nKeyToUse
));
252 if (xFormatProps
.is())
254 css::lang::Locale loc
;
255 if (xFormatProps
->getPropertyValue("Locale") >>= loc
)
256 nStandardKey
= xFormatTypes
->getStandardIndex(loc
);
264 SAL_WARN("connectivity.commontools", "no format by key " << nKeyToUse
);
271 // Why use nStandardKey rather than nKeyToUse here? I'm not sure, but "it was always like that".
272 // Previously had hardcoded 0 instead of nStandardKey, which led to problems with dates
273 // because of differences M/D/Y vs D/M/Y. This at least fixes those problems, but possibly
274 // nKeyToUse is an even better choice than nStandardKey.
275 // OTOH, using nKeyToUse nullifies the special treatment for percent formats,
276 // leading to "5" (in a percent format) to be understood as "500%" instead of "5%".
277 sal_Int32 nRealUsedKey
= xFormatter
->detectNumberFormat(nStandardKey
, rString
);
278 if (nRealUsedKey
!= nKeyToUse
)
279 nRealUsedTypeClass
= getNumberFormatType(xFormatter
, nRealUsedKey
) & ~NumberFormat::DEFINED
;
281 // and again a special treatment, this time for percent formats
282 if ((NumberFormat::NUMBER
== nRealUsedTypeClass
) && (NumberFormat::PERCENT
== nTypeClass
))
283 { // formatting should be "percent", but the String provides just a simple number -> adjust
284 OUString sExpanded
= rString
+ "%";
285 fValue
= xFormatter
->convertStringToNumber(nKeyToUse
, sExpanded
);
288 switch (nRealUsedTypeClass
)
290 case NumberFormat::DATE
:
291 case NumberFormat::DATETIME
:
292 case NumberFormat::TIME
:
293 DBTypeConversion::setValue(xVariant
,rNullDate
,fValue
,nRealUsedTypeClass
);
295 case NumberFormat::CURRENCY
:
296 case NumberFormat::NUMBER
:
297 case NumberFormat::SCIENTIFIC
:
298 case NumberFormat::FRACTION
:
299 case NumberFormat::PERCENT
:
300 xVariant
->updateDouble(fValue
);
303 xVariant
->updateString(rString
);
306 catch(const Exception
& )
308 xVariant
->updateString(rString
);
315 case css::sdbc::DataType::CHAR
:
316 case css::sdbc::DataType::VARCHAR
:
317 case css::sdbc::DataType::LONGVARCHAR
:
318 xVariant
->updateString(rString
);
321 xVariant
->updateNull();
327 void DBTypeConversion::setValue(const Reference
<XColumnUpdate
>& xVariant
,
328 const Date
& rNullDate
,
329 const double& rValue
,
332 switch (nKeyType
& ~NumberFormat::DEFINED
)
334 case NumberFormat::DATE
:
335 xVariant
->updateDate(toDate( rValue
, rNullDate
));
337 case NumberFormat::DATETIME
:
338 xVariant
->updateTimestamp(toDateTime(rValue
,rNullDate
));
340 case NumberFormat::TIME
:
341 xVariant
->updateTime(toTime(rValue
));
345 double nValue
= rValue
;
346 // Reference<XPropertySet> xProp(xVariant,UNO_QUERY);
348 // && xProp->getPropertySetInfo()->hasPropertyByName(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISSIGNED))
349 // && !::comphelper::getBOOL(xProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISSIGNED))) )
351 // switch (::comphelper::getINT32(xProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))))
353 // case DataType::TINYINT:
354 // nValue = static_cast<sal_uInt8>(rValue);
356 // case DataType::SMALLINT:
357 // nValue = static_cast<sal_uInt16>(rValue);
359 // case DataType::INTEGER:
360 // nValue = static_cast<sal_uInt32>(rValue);
362 // case DataType::BIGINT:
363 // nValue = static_cast<sal_uInt64>(rValue);
367 xVariant
->updateDouble(nValue
);
373 double DBTypeConversion::getValue( const Reference
< XColumn
>& i_column
, const Date
& i_relativeToNullDate
)
377 const Reference
< XPropertySet
> xProp( i_column
, UNO_QUERY_THROW
);
379 const sal_Int32 nColumnType
= ::comphelper::getINT32( xProp
->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_TYPE
) ) );
380 switch ( nColumnType
)
383 return toDouble( i_column
->getDate(), i_relativeToNullDate
);
386 return toDouble( i_column
->getTime() );
388 case DataType::TIMESTAMP
:
389 return toDouble( i_column
->getTimestamp(), i_relativeToNullDate
);
393 bool bIsSigned
= true;
394 OSL_VERIFY( xProp
->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_ISSIGNED
) ) >>= bIsSigned
);
397 switch ( nColumnType
)
399 case DataType::TINYINT
:
400 return static_cast<double>(static_cast<sal_uInt8
>(i_column
->getByte()));
401 case DataType::SMALLINT
:
402 return static_cast<double>(static_cast<sal_uInt16
>(i_column
->getShort()));
403 case DataType::INTEGER
:
404 return static_cast<double>(static_cast<sal_uInt32
>(i_column
->getInt()));
405 case DataType::BIGINT
:
406 return static_cast<double>(static_cast<sal_uInt64
>(i_column
->getLong()));
410 return i_column
->getDouble();
413 catch( const Exception
& )
415 DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
420 OUString
DBTypeConversion::getFormattedValue(const Reference
< XPropertySet
>& _xColumn
,
421 const Reference
<XNumberFormatter
>& _xFormatter
,
422 const css::lang::Locale
& _rLocale
,
423 const Date
& _rNullDate
)
425 OSL_ENSURE(_xColumn
.is() && _xFormatter
.is(), "DBTypeConversion::getFormattedValue: invalid arg !");
426 if (!_xColumn
.is() || !_xFormatter
.is())
432 _xColumn
->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY
)) >>= nKey
;
434 catch (const Exception
& )
436 OSL_FAIL("DBTypeConversion::getValue: caught an exception while asking for the format key!");
441 Reference
<XNumberFormats
> xFormats( _xFormatter
->getNumberFormatsSupplier()->getNumberFormats() );
443 nKey
= ::dbtools::getDefaultNumberFormat(_xColumn
,
444 Reference
< XNumberFormatTypes
> (xFormats
, UNO_QUERY
),
449 sal_Int16 nKeyType
= getNumberFormatType(_xFormatter
, nKey
) & ~NumberFormat::DEFINED
;
451 return DBTypeConversion::getFormattedValue(Reference
< XColumn
> (_xColumn
, UNO_QUERY
), _xFormatter
, _rNullDate
, nKey
, nKeyType
);
455 OUString
DBTypeConversion::getFormattedValue(const Reference
<XColumn
>& xVariant
,
456 const Reference
<XNumberFormatter
>& xFormatter
,
457 const Date
& rNullDate
,
466 switch (nKeyType
& ~NumberFormat::DEFINED
)
468 case NumberFormat::DATE
:
469 case NumberFormat::DATETIME
:
471 // get a value which represents the given date, relative to the given null date
472 double fValue
= getValue( xVariant
, rNullDate
);
473 if ( !xVariant
->wasNull() )
475 // get the null date of the formatter
476 Date
aFormatterNullDate( rNullDate
);
479 Reference
< XNumberFormatsSupplier
> xSupplier( xFormatter
->getNumberFormatsSupplier(), UNO_SET_THROW
);
480 Reference
< XPropertySet
> xFormatterSettings( xSupplier
->getNumberFormatSettings(), UNO_SET_THROW
);
481 OSL_VERIFY( xFormatterSettings
->getPropertyValue("NullDate") >>= aFormatterNullDate
);
483 catch( const Exception
& )
485 DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
487 // get a value which represents the given date, relative to the null date of the formatter
488 fValue
-= toDays( rNullDate
, aFormatterNullDate
);
490 aString
= xFormatter
->convertNumberToString( nKey
, fValue
);
494 case NumberFormat::TIME
:
495 case NumberFormat::NUMBER
:
496 case NumberFormat::SCIENTIFIC
:
497 case NumberFormat::FRACTION
:
498 case NumberFormat::PERCENT
:
500 double fValue
= xVariant
->getDouble();
501 if (!xVariant
->wasNull())
502 aString
= xFormatter
->convertNumberToString(nKey
, fValue
);
504 case NumberFormat::CURRENCY
:
506 double fValue
= xVariant
->getDouble();
507 if (!xVariant
->wasNull())
508 aString
= xFormatter
->getInputString(nKey
, fValue
);
510 case NumberFormat::TEXT
:
511 aString
= xFormatter
->formatString(nKey
, xVariant
->getString());
514 aString
= xVariant
->getString();
517 catch(const Exception
& )
519 aString
= xVariant
->getString();
526 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */