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 <config_features.h>
23 #include <tools/urlobj.hxx>
24 #include <svl/converter.hxx>
25 #include <comphelper/processfactory.hxx>
26 #include <comphelper/string.hxx>
27 #include <comphelper/types.hxx>
28 #include <ucbhelper/content.hxx>
29 #include <svx/txenctab.hxx>
30 #include <unotools/sharedunocomponent.hxx>
32 #if HAVE_FEATURE_DBCONNECTIVITY
33 #include <svx/dbcharsethelper.hxx>
36 #include <com/sun/star/sdb/CommandType.hpp>
37 #include <com/sun/star/sdbc/DataType.hpp>
38 #include <com/sun/star/sdbc/XConnection.hpp>
39 #include <com/sun/star/sdbc/XDriver.hpp>
40 #include <com/sun/star/sdbc/XDriverAccess.hpp>
41 #include <com/sun/star/sdbc/DriverManager.hpp>
42 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
43 #include <com/sun/star/sdbc/XRow.hpp>
44 #include <com/sun/star/sdbc/XRowSet.hpp>
45 #include <com/sun/star/sdbc/XRowUpdate.hpp>
46 #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
47 #include <com/sun/star/sdbcx/XAppend.hpp>
48 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
49 #include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp>
50 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
51 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
52 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
53 #include <com/sun/star/beans/XPropertySet.hpp>
54 #include <com/sun/star/container/XEnumerationAccess.hpp>
55 #include <com/sun/star/lang/XComponent.hpp>
56 #include <com/sun/star/ucb/NameClash.hpp>
57 #include <com/sun/star/ucb/TransferInfo.hpp>
58 #include <com/sun/star/ucb/XCommandInfo.hpp>
60 #include "scerrors.hxx"
63 #include "progress.hxx"
64 #include "formulacell.hxx"
65 #include "editutil.hxx"
66 #include "cellform.hxx"
67 #include "dbdocutl.hxx"
68 #include "dociter.hxx"
69 #include "globstr.hrc"
70 #include <svl/zformat.hxx>
71 #include <svl/intitem.hxx>
72 #include "patattr.hxx"
73 #include "scitems.hxx"
74 #include "docpool.hxx"
75 #include "segmenttree.hxx"
76 #include "docparam.hxx"
77 #include "cellvalue.hxx"
79 #include <unordered_set>
82 using namespace com::sun::star
;
85 #if HAVE_FEATURE_DBCONNECTIVITY
87 #define SC_SERVICE_ROWSET "com.sun.star.sdb.RowSet"
89 //! move to a header file?
90 #define SC_DBPROP_ACTIVECONNECTION "ActiveConnection"
91 #define SC_DBPROP_COMMAND "Command"
92 #define SC_DBPROP_COMMANDTYPE "CommandType"
93 #define SC_DBPROP_PROPCHANGE_NOTIFY "PropertyChangeNotificationEnabled"
95 #define SC_DBPROP_NAME "Name"
96 #define SC_DBPROP_TYPE "Type"
97 #define SC_DBPROP_PRECISION "Precision"
98 #define SC_DBPROP_SCALE "Scale"
100 #define SC_DBPROP_EXTENSION "Extension"
101 #define SC_DBPROP_CHARSET "CharSet"
105 sal_uLong
lcl_getDBaseConnection(uno::Reference
<sdbc::XDriverManager2
>& _rDrvMgr
, uno::Reference
<sdbc::XConnection
>& _rConnection
, OUString
& _rTabName
, const OUString
& rFullFileName
, rtl_TextEncoding eCharSet
)
108 aURL
.SetSmartProtocol( INetProtocol::File
);
109 aURL
.SetSmartURL( rFullFileName
);
110 _rTabName
= aURL
.getBase( INetURLObject::LAST_SEGMENT
, true,
111 INetURLObject::DECODE_UNAMBIGUOUS
);
112 OUString aExtension
= aURL
.getExtension();
113 aURL
.removeSegment();
114 aURL
.removeFinalSlash();
115 OUString aPath
= aURL
.GetMainURL(INetURLObject::NO_DECODE
);
116 uno::Reference
<uno::XComponentContext
> xContext
= comphelper::getProcessComponentContext();
118 _rDrvMgr
.set( sdbc::DriverManager::create( xContext
) );
122 OUString
aConnUrl("sdbc:dbase:");
125 ::std::vector
< rtl_TextEncoding
> aEncodings
;
126 svxform::charset_helper::getSupportedTextEncodings( aEncodings
);
127 ::std::vector
< rtl_TextEncoding
>::iterator aIter
= ::std::find(aEncodings
.begin(),aEncodings
.end(),(rtl_TextEncoding
) eCharSet
);
128 if ( aIter
== aEncodings
.end() )
130 OSL_FAIL( "DBaseImport: dbtools::OCharsetMap doesn't know text encoding" );
131 return SCERR_IMPORT_CONNECT
;
132 } // if ( aIter == aMap.end() )
133 OUString aCharSetStr
;
134 if ( RTL_TEXTENCODING_DONTKNOW
!= *aIter
)
135 { // it's not the virtual "system charset"
136 const char* pIanaName
= rtl_getMimeCharsetFromTextEncoding( *aIter
);
137 OSL_ENSURE( pIanaName
, "invalid mime name!" );
139 aCharSetStr
= OUString::createFromAscii( pIanaName
);
142 uno::Sequence
<beans::PropertyValue
> aProps(2);
143 aProps
[0].Name
= SC_DBPROP_EXTENSION
;
144 aProps
[0].Value
<<= OUString( aExtension
);
145 aProps
[1].Name
= SC_DBPROP_CHARSET
;
146 aProps
[1].Value
<<= aCharSetStr
;
148 _rConnection
= _rDrvMgr
->getConnectionWithInfo( aConnUrl
, aProps
);
153 #endif // HAVE_FEATURE_DBCONNECTIVITY
155 // MoveFile/KillFile/IsDocument: similar to SfxContentHelper
157 bool ScDocShell::MoveFile( const INetURLObject
& rSourceObj
, const INetURLObject
& rDestObj
)
159 bool bMoveData
= true;
160 bool bRet
= true, bKillSource
= false;
161 if ( rSourceObj
.GetProtocol() != rDestObj
.GetProtocol() )
166 OUString aName
= rDestObj
.getName();
167 INetURLObject aDestPathObj
= rDestObj
;
168 aDestPathObj
.removeSegment();
169 aDestPathObj
.setFinalSlash();
173 ::ucbhelper::Content
aDestPath( aDestPathObj
.GetMainURL(INetURLObject::NO_DECODE
),
174 uno::Reference
< ::com::sun::star::ucb::XCommandEnvironment
>(),
175 comphelper::getProcessComponentContext() );
176 uno::Reference
< ::com::sun::star::ucb::XCommandInfo
> xInfo
= aDestPath
.getCommands();
177 OUString aTransferName
= "transfer";
178 if ( xInfo
->hasCommandByName( aTransferName
) )
180 aDestPath
.executeCommand( aTransferName
, uno::makeAny(
181 ::com::sun::star::ucb::TransferInfo( bMoveData
, rSourceObj
.GetMainURL(INetURLObject::NO_DECODE
), aName
,
182 ::com::sun::star::ucb::NameClash::ERROR
) ) );
186 OSL_FAIL( "transfer command not available" );
189 catch( uno::Exception
& )
191 // ucb may throw different exceptions on failure now
196 KillFile( rSourceObj
);
201 bool ScDocShell::KillFile( const INetURLObject
& rURL
)
206 ::ucbhelper::Content
aCnt( rURL
.GetMainURL(INetURLObject::NO_DECODE
),
207 uno::Reference
< ::com::sun::star::ucb::XCommandEnvironment
>(),
208 comphelper::getProcessComponentContext() );
209 aCnt
.executeCommand( OUString( "delete" ),
210 comphelper::makeBoolAny( true ) );
212 catch( uno::Exception
& )
214 // ucb may throw different exceptions on failure now
221 bool ScDocShell::IsDocument( const INetURLObject
& rURL
)
226 ::ucbhelper::Content
aCnt( rURL
.GetMainURL(INetURLObject::NO_DECODE
),
227 uno::Reference
< ::com::sun::star::ucb::XCommandEnvironment
>(),
228 comphelper::getProcessComponentContext() );
229 bRet
= aCnt
.isDocument();
231 catch( uno::Exception
& )
233 // ucb may throw different exceptions on failure now - warning only
234 OSL_FAIL( "Any other exception" );
240 #if HAVE_FEATURE_DBCONNECTIVITY
242 static void lcl_setScalesToColumns(ScDocument
& rDoc
, const vector
<long>& rScales
)
244 SvNumberFormatter
* pFormatter
= rDoc
.GetFormatTable();
248 SCCOL nColCount
= static_cast<SCCOL
>(rScales
.size());
249 for (SCCOL i
= 0; i
< nColCount
; ++i
)
254 sal_uInt32 nOldFormat
;
255 rDoc
.GetNumberFormat(static_cast<SCCOL
>(i
), 0, 0, nOldFormat
);
256 const SvNumberformat
* pOldEntry
= pFormatter
->GetEntry(nOldFormat
);
260 LanguageType eLang
= pOldEntry
->GetLanguage();
261 bool bThousand
, bNegRed
;
262 sal_uInt16 nPrecision
, nLeading
;
263 pOldEntry
->GetFormatSpecialInfo(bThousand
, bNegRed
, nPrecision
, nLeading
);
265 nPrecision
= static_cast<sal_uInt16
>(rScales
[i
]);
266 OUString aNewPicture
= pFormatter
->GenerateFormat(nOldFormat
, eLang
,
267 bThousand
, bNegRed
, nPrecision
, nLeading
);
269 sal_uInt32 nNewFormat
= pFormatter
->GetEntryKey(aNewPicture
, eLang
);
270 if (nNewFormat
== NUMBERFORMAT_ENTRY_NOT_FOUND
)
272 sal_Int32 nErrPos
= 0;
274 bool bOk
= pFormatter
->PutEntry(
275 aNewPicture
, nErrPos
, nNewType
, nNewFormat
, eLang
);
281 ScPatternAttr
aNewAttrs( rDoc
.GetPool() );
282 SfxItemSet
& rSet
= aNewAttrs
.GetItemSet();
283 rSet
.Put( SfxUInt32Item(ATTR_VALUE_FORMAT
, nNewFormat
) );
284 rDoc
.ApplyPatternAreaTab(static_cast<SCCOL
>(i
), 0, static_cast<SCCOL
>(i
), MAXROW
, 0, aNewAttrs
);
288 #endif // HAVE_FEATURE_DBCONNECTIVITY
290 sal_uLong
ScDocShell::DBaseImport( const OUString
& rFullFileName
, rtl_TextEncoding eCharSet
,
291 ScColWidthParam aColWidthParam
[MAXCOLCOUNT
], ScFlatBoolRowSegments
& rRowHeightsRecalc
)
293 #if !HAVE_FEATURE_DBCONNECTIVITY
294 (void) rFullFileName
;
296 (void) aColWidthParam
;
297 (void) rRowHeightsRecalc
;
299 return ERRCODE_IO_GENERAL
;
302 sal_uLong nErr
= eERR_OK
;
304 // Try to get the Text Encoding from the driver
305 if( eCharSet
== RTL_TEXTENCODING_IBM_850
)
306 eCharSet
= RTL_TEXTENCODING_DONTKNOW
;
313 uno::Reference
<sdbc::XDriverManager2
> xDrvMan
;
314 uno::Reference
<sdbc::XConnection
> xConnection
;
315 sal_uLong nRet
= lcl_getDBaseConnection(xDrvMan
,xConnection
,aTabName
,rFullFileName
,eCharSet
);
316 if ( !xConnection
.is() || !xDrvMan
.is() )
318 ::utl::DisposableComponent
aConnectionHelper(xConnection
);
320 ScProgress
aProgress( this, ScGlobal::GetRscString( STR_LOAD_DOC
), 0 );
321 uno::Reference
<lang::XMultiServiceFactory
> xFactory
= comphelper::getProcessServiceFactory();
322 uno::Reference
<sdbc::XRowSet
> xRowSet( xFactory
->createInstance(
323 OUString( SC_SERVICE_ROWSET
) ),
325 ::utl::DisposableComponent
aRowSetHelper(xRowSet
);
326 uno::Reference
<beans::XPropertySet
> xRowProp( xRowSet
, uno::UNO_QUERY
);
327 OSL_ENSURE( xRowProp
.is(), "can't get RowSet" );
328 if (!xRowProp
.is()) return SCERR_IMPORT_CONNECT
;
330 sal_Int32 nType
= sdb::CommandType::TABLE
;
333 aAny
<<= xConnection
;
334 xRowProp
->setPropertyValue( OUString(SC_DBPROP_ACTIVECONNECTION
), aAny
);
337 xRowProp
->setPropertyValue( OUString(SC_DBPROP_COMMANDTYPE
), aAny
);
339 aAny
<<= OUString( aTabName
);
340 xRowProp
->setPropertyValue( OUString(SC_DBPROP_COMMAND
), aAny
);
343 xRowProp
->setPropertyValue( OUString(SC_DBPROP_PROPCHANGE_NOTIFY
), aAny
);
347 uno::Reference
<sdbc::XResultSetMetaData
> xMeta
;
348 uno::Reference
<sdbc::XResultSetMetaDataSupplier
> xMetaSupp( xRowSet
, uno::UNO_QUERY
);
349 if ( xMetaSupp
.is() )
350 xMeta
= xMetaSupp
->getMetaData();
352 nColCount
= xMeta
->getColumnCount(); // this is the number of real columns
354 if ( nColCount
> MAXCOL
+1 )
356 nColCount
= MAXCOL
+1;
357 nErr
= SCWARN_IMPORT_COLUMN_OVERFLOW
; // warning
360 uno::Reference
<sdbc::XRow
> xRow( xRowSet
, uno::UNO_QUERY
);
361 OSL_ENSURE( xRow
.is(), "can't get Row" );
362 if (!xRow
.is()) return SCERR_IMPORT_CONNECT
;
364 // currency flag is not needed for dBase
365 uno::Sequence
<sal_Int32
> aColTypes( nColCount
); // column types
366 sal_Int32
* pTypeArr
= aColTypes
.getArray();
367 for (i
=0; i
<nColCount
; i
++)
368 pTypeArr
[i
] = xMeta
->getColumnType( i
+1 );
371 //! add type descriptions
373 aProgress
.SetState( 0 );
375 vector
<long> aScales(nColCount
, -1);
376 for (i
=0; i
<nColCount
; i
++)
378 OUString aHeader
= xMeta
->getColumnLabel( i
+1 );
380 switch ( pTypeArr
[i
] )
382 case sdbc::DataType::BIT
:
385 case sdbc::DataType::DATE
:
388 case sdbc::DataType::LONGVARCHAR
:
391 case sdbc::DataType::VARCHAR
:
392 aHeader
+= ",C," + OUString::number( xMeta
->getColumnDisplaySize( i
+1 ) );
394 case sdbc::DataType::DECIMAL
:
396 long nPrec
= xMeta
->getPrecision( i
+1 );
397 long nScale
= xMeta
->getScale( i
+1 );
400 SvDbaseConverter::ConvertPrecisionToDbase(
403 OUString::number( nScale
);
409 aDocument
.SetString( static_cast<SCCOL
>(i
), 0, 0, aHeader
);
412 lcl_setScalesToColumns(aDocument
, aScales
);
414 SCROW nRow
= 1; // 0 is column titles
416 while ( !bEnd
&& xRowSet
->next() )
418 if ( nRow
<= MAXROW
)
420 bool bSimpleRow
= true;
422 for (i
=0; i
<nColCount
; i
++)
424 ScDatabaseDocUtil::StrData aStrData
;
425 ScDatabaseDocUtil::PutData( &aDocument
, nCol
, nRow
, 0,
426 xRow
, i
+1, pTypeArr
[i
], false,
429 if (aStrData
.mnStrLength
> aColWidthParam
[nCol
].mnMaxTextLen
)
431 aColWidthParam
[nCol
].mnMaxTextLen
= aStrData
.mnStrLength
;
432 aColWidthParam
[nCol
].mnMaxTextRow
= nRow
;
435 if (!aStrData
.mbSimpleText
)
438 aColWidthParam
[nCol
].mbSimpleText
= false;
444 rRowHeightsRecalc
.setTrue(nRow
, nRow
);
447 else // past the end of the spreadsheet
449 bEnd
= true; // don't continue
450 nErr
= SCWARN_IMPORT_RANGE_OVERFLOW
; // warning message
454 catch ( sdbc::SQLException
& )
456 nErr
= SCERR_IMPORT_CONNECT
;
458 catch ( uno::Exception
& )
460 OSL_FAIL("Unexpected exception in database");
461 nErr
= ERRCODE_IO_GENERAL
;
465 #endif // HAVE_FEATURE_DBCONNECTIVITY
468 #if HAVE_FEATURE_DBCONNECTIVITY
472 inline bool IsAsciiDigit( sal_Unicode c
)
474 return 0x30 <= c
&& c
<= 0x39;
477 inline bool IsAsciiAlpha( sal_Unicode c
)
479 return (0x41 <= c
&& c
<= 0x5a) || (0x61 <= c
&& c
<= 0x7a);
482 void lcl_GetColumnTypes(
483 ScDocShell
& rDocShell
, const ScRange
& rDataRange
, bool bHasFieldNames
,
484 OUString
* pColNames
, sal_Int32
* pColTypes
, sal_Int32
* pColLengths
,
485 sal_Int32
* pColScales
, bool& bHasMemo
, rtl_TextEncoding eCharSet
)
487 ScDocument
& rDoc
= rDocShell
.GetDocument();
488 SvNumberFormatter
* pNumFmt
= rDoc
.GetFormatTable();
490 SCTAB nTab
= rDataRange
.aStart
.Tab();
491 SCCOL nFirstCol
= rDataRange
.aStart
.Col();
492 SCROW nFirstRow
= rDataRange
.aStart
.Row();
493 SCCOL nLastCol
= rDataRange
.aEnd
.Col();
494 SCROW nLastRow
= rDataRange
.aEnd
.Row();
496 typedef std::unordered_set
<OUString
, OUStringHash
> StrSetType
;
497 StrSetType aFieldNames
;
500 SCROW nFirstDataRow
= ( bHasFieldNames
? nFirstRow
+ 1 : nFirstRow
);
501 for ( SCCOL nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++ )
503 bool bTypeDefined
= false;
504 bool bPrecDefined
= false;
505 sal_Int32 nFieldLen
= 0;
506 sal_Int32 nPrecision
= 0;
507 sal_Int32 nDbType
= sdbc::DataType::SQLNULL
;
511 // Fieldname[,Type[,Width[,Prec]]]
512 // Type etc.: L; D; C[,W]; N[,W[,P]]
513 if ( bHasFieldNames
)
515 aString
= rDoc
.GetString(nCol
, nFirstRow
, nTab
);
516 aString
= aString
.toAsciiUpperCase();
517 sal_Int32 nToken
= comphelper::string::getTokenCount(aString
, ',');
520 aFieldName
= aString
.getToken( 0, ',' );
521 aString
= comphelper::string::remove(aString
, ' ');
522 switch ( aString
.getToken( 1, ',' )[0] )
525 nDbType
= sdbc::DataType::BIT
;
531 nDbType
= sdbc::DataType::DATE
;
537 nDbType
= sdbc::DataType::LONGVARCHAR
;
544 nDbType
= sdbc::DataType::VARCHAR
;
549 nDbType
= sdbc::DataType::DECIMAL
;
553 if ( bTypeDefined
&& !nFieldLen
&& nToken
> 2 )
555 nFieldLen
= aString
.getToken( 2, ',' ).toInt32();
556 if ( !bPrecDefined
&& nToken
> 3 )
558 OUString
aTmp( aString
.getToken( 3, ',' ) );
559 if ( CharClass::isAsciiNumeric(aTmp
) )
561 nPrecision
= aTmp
.toInt32();
562 if (nPrecision
&& nFieldLen
< nPrecision
+1)
563 nFieldLen
= nPrecision
+ 1; // include decimal separator
570 aFieldName
= aString
;
572 // Check field name and generate valid field name if necessary.
573 // First character has to be alphabetical, subsequent characters
574 // have to be alphanumerical or underscore.
575 // "_DBASELOCK" is reserved (obsolete because first character is
576 // not alphabetical).
577 // No duplicated names.
578 if ( !IsAsciiAlpha( aFieldName
[0] ) )
579 aFieldName
= "N" + aFieldName
;
582 for ( const sal_Unicode
* p
= aFieldName
.getStr(); ( c
= *p
) != 0; p
++ )
584 if ( IsAsciiAlpha( c
) || IsAsciiDigit( c
) || c
== '_' )
585 aTmpStr
+= OUString(c
);
589 aFieldName
= aTmpStr
;
590 if ( aFieldName
.getLength() > 10 )
591 aFieldName
= aFieldName
.copy(0, 10);
593 if (!aFieldNames
.insert(aFieldName
).second
)
594 { // Duplicated field name, append numeric suffix.
596 OUString
aFixPart( aFieldName
);
600 OUString aVarPart
= OUString::number( nSub
);
601 if ( aFixPart
.getLength() + aVarPart
.getLength() > 10 )
602 aFixPart
= aFixPart
.copy( 0, 10 - aVarPart
.getLength() );
603 aFieldName
= aFixPart
;
604 aFieldName
+= aVarPart
;
605 } while (!aFieldNames
.insert(aFieldName
).second
);
610 aFieldName
= "N" + OUString::number(nCol
+1);
615 ScRefCellValue aCell
;
616 aCell
.assign(rDoc
, ScAddress(nCol
, nFirstDataRow
, nTab
));
617 if (aCell
.isEmpty() || aCell
.hasString())
618 nDbType
= sdbc::DataType::VARCHAR
;
622 rDoc
.GetNumberFormat( nCol
, nFirstDataRow
, nTab
, nFormat
);
623 switch ( pNumFmt
->GetType( nFormat
) )
625 case css::util::NumberFormat::LOGICAL
:
626 nDbType
= sdbc::DataType::BIT
;
629 case css::util::NumberFormat::DATE
:
630 nDbType
= sdbc::DataType::DATE
;
633 case css::util::NumberFormat::TIME
:
634 case css::util::NumberFormat::DATETIME
:
635 nDbType
= sdbc::DataType::VARCHAR
;
638 nDbType
= sdbc::DataType::DECIMAL
;
642 bool bSdbLenAdjusted
= false;
643 bool bSdbLenBad
= false;
645 if ( nDbType
== sdbc::DataType::VARCHAR
&& !nFieldLen
)
646 { // Determine maximum field width.
647 nFieldLen
= rDoc
.GetMaxStringLen( nTab
, nCol
, nFirstDataRow
,
648 nLastRow
, eCharSet
);
649 if ( nFieldLen
== 0 )
652 else if ( nDbType
== sdbc::DataType::DECIMAL
)
653 { // Determine maximum field width and precision.
656 nLen
= rDoc
.GetMaxNumberStringLen( nPrec
, nTab
, nCol
,
657 nFirstDataRow
, nLastRow
);
658 // dBaseIII precision limit: 15
659 if ( nPrecision
> 15 )
663 if ( bPrecDefined
&& nPrecision
!= nPrec
)
665 if (nPrecision
< nPrec
)
667 // This is a hairy case. User defined nPrecision but a
668 // number format has more precision. Modifying a dBase
669 // field may as well render the resulting file useless for
670 // an application that relies on its defined structure,
671 // especially if we are resaving an already existing file.
672 // So who's right, the user who (or the loaded file that)
673 // defined the field, or the user who applied the format?
674 // Commit f59e350d1733125055f1144f8b3b1b0a46f6d1ca gave the
675 // format a higher priority, which is debatable.
676 SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field precision for "
677 << aFieldName
<< " (" << nPrecision
<< "<" << nPrec
<< ")");
679 // Adjust length to larger predefined integer part. There
680 // may be a reason that the field was prepared for larger
682 if (nFieldLen
- nPrecision
> nLen
- nPrec
)
683 nLen
= nFieldLen
- (nPrecision
? nPrecision
+1 : 0) + 1 + nPrec
;
684 // And override precision.
690 // Adjust length to predefined precision.
691 nLen
= nLen
+ ( nPrecision
- nPrec
);
693 /* If the above override for (nPrecision < nPrec) was not in place then
694 * nPrecision could be 0 and this would be the code path to correctly
695 * calculate nLen. But as is, nPrecision is never 0 here, see CID#982304 */
697 // Adjust length to predefined precision.
699 nLen
= nLen
+ ( nPrecision
- nPrec
);
701 nLen
-= nPrec
+1; // also remove the decimal separator
705 if (nFieldLen
< nLen
)
711 // Again a hairy case and conflict. Furthermore, the
712 // larger overall length may be a result of only a higher
713 // precision obtained from formats.
714 SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field length for "
715 << aFieldName
<< " (" << nFieldLen
<< "<" << nLen
<< ")");
721 if ( nFieldLen
== 0 )
723 else if ( nFieldLen
> 19 )
724 nFieldLen
= 19; // dBaseIII numeric field length limit: 19
725 if ( nPrecision
&& nFieldLen
< nPrecision
+ 2 )
726 nFieldLen
= nPrecision
+ 2; // 0. must fit into
727 // 538 MUST: Sdb internal representation adds 2 to the field length!
728 // To give the user what he wants we must subtract it here.
729 //! CAVEAT! There is no way to define a numeric field with a length
730 //! of 1 and no decimals!
731 if ( nFieldLen
== 1 && nPrecision
== 0 )
733 nFieldLen
= SvDbaseConverter::ConvertPrecisionToOdbc( nFieldLen
, nPrecision
);
734 bSdbLenAdjusted
= true;
736 if ( nFieldLen
> 254 )
738 if ( nDbType
== sdbc::DataType::VARCHAR
)
739 { // Too long for a normal text field => memo field.
740 nDbType
= sdbc::DataType::LONGVARCHAR
;
745 nFieldLen
= 254; // bad luck..
748 pColNames
[nField
] = aFieldName
;
749 pColTypes
[nField
] = nDbType
;
750 pColLengths
[nField
] = nFieldLen
;
751 pColScales
[nField
] = nPrecision
;
753 // undo change to field length, reflect reality
754 if ( bSdbLenAdjusted
)
756 nFieldLen
= SvDbaseConverter::ConvertPrecisionToDbase( nFieldLen
, nPrecision
);
757 if ( bSdbLenBad
&& nFieldLen
== 1 )
758 nFieldLen
= 2; // THIS is reality
764 inline void lcl_getLongVarCharEditString( OUString
& rString
,
765 const ScRefCellValue
& rCell
, ScFieldEditEngine
& rEditEngine
)
767 if (!rCell
.mpEditText
)
770 rEditEngine
.SetText(*rCell
.mpEditText
);
771 rString
= rEditEngine
.GetText( LINEEND_CRLF
);
774 inline void lcl_getLongVarCharString(
775 OUString
& rString
, ScDocument
& rDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
, SvNumberFormatter
& rNumFmt
)
778 ScAddress
aPos(nCol
, nRow
, nTab
);
779 sal_uInt32 nFormat
= rDoc
.GetNumberFormat(aPos
);
780 rString
= ScCellFormat::GetString(rDoc
, aPos
, nFormat
, &pColor
, rNumFmt
);
785 #endif // HAVE_FEATURE_DBCONNECTIVITY
787 sal_uLong
ScDocShell::DBaseExport( const OUString
& rFullFileName
, rtl_TextEncoding eCharSet
, bool& bHasMemo
)
789 #if !HAVE_FEATURE_DBCONNECTIVITY
790 (void) rFullFileName
;
794 return ERRCODE_IO_GENERAL
;
796 // remove the file so the dBase driver doesn't find an invalid file
797 INetURLObject
aDeleteObj( rFullFileName
, INetProtocol::File
);
798 KillFile( aDeleteObj
);
800 sal_uLong nErr
= eERR_OK
;
803 SCCOL nFirstCol
, nLastCol
;
804 SCROW nFirstRow
, nLastRow
;
805 SCTAB nTab
= GetSaveTab();
806 aDocument
.GetDataStart( nTab
, nFirstCol
, nFirstRow
);
807 aDocument
.GetCellArea( nTab
, nLastCol
, nLastRow
);
808 if ( nFirstCol
> nLastCol
)
809 nFirstCol
= nLastCol
;
810 if ( nFirstRow
> nLastRow
)
811 nFirstRow
= nLastRow
;
812 ScProgress
aProgress( this, ScGlobal::GetRscString( STR_SAVE_DOC
),
813 nLastRow
- nFirstRow
);
814 SvNumberFormatter
* pNumFmt
= aDocument
.GetFormatTable();
816 bool bHasFieldNames
= true;
817 for ( SCCOL nDocCol
= nFirstCol
; nDocCol
<= nLastCol
&& bHasFieldNames
; nDocCol
++ )
818 { // nur Strings in erster Zeile => sind Feldnamen
819 if ( !aDocument
.HasStringData( nDocCol
, nFirstRow
, nTab
) )
820 bHasFieldNames
= false;
823 long nColCount
= nLastCol
- nFirstCol
+ 1;
824 uno::Sequence
<OUString
> aColNames( nColCount
);
825 uno::Sequence
<sal_Int32
> aColTypes( nColCount
);
826 uno::Sequence
<sal_Int32
> aColLengths( nColCount
);
827 uno::Sequence
<sal_Int32
> aColScales( nColCount
);
829 ScRange
aDataRange( nFirstCol
, nFirstRow
, nTab
, nLastCol
, nLastRow
, nTab
);
830 lcl_GetColumnTypes( *this, aDataRange
, bHasFieldNames
,
831 aColNames
.getArray(), aColTypes
.getArray(),
832 aColLengths
.getArray(), aColScales
.getArray(),
833 bHasMemo
, eCharSet
);
834 // also needed for exception catch
836 ScFieldEditEngine
aEditEngine(&aDocument
, aDocument
.GetEditPool());
842 uno::Reference
<sdbc::XDriverManager2
> xDrvMan
;
843 uno::Reference
<sdbc::XConnection
> xConnection
;
844 sal_uLong nRet
= lcl_getDBaseConnection(xDrvMan
,xConnection
,aTabName
,rFullFileName
,eCharSet
);
845 if ( !xConnection
.is() || !xDrvMan
.is() )
847 ::utl::DisposableComponent
aConnectionHelper(xConnection
);
850 uno::Reference
< sdbc::XDriverAccess
> xAccess(xDrvMan
,uno::UNO_QUERY
);
851 uno::Reference
< sdbcx::XDataDefinitionSupplier
> xDDSup( xAccess
->getDriverByURL( xConnection
->getMetaData()->getURL() ), uno::UNO_QUERY
);
853 return SCERR_EXPORT_CONNECT
;
856 uno::Reference
<sdbcx::XTablesSupplier
> xTablesSupp
=xDDSup
->getDataDefinitionByConnection( xConnection
);
857 OSL_ENSURE( xTablesSupp
.is(), "can't get Data Definition" );
858 if (!xTablesSupp
.is()) return SCERR_EXPORT_CONNECT
;
860 uno::Reference
<container::XNameAccess
> xTables
= xTablesSupp
->getTables();
861 OSL_ENSURE( xTables
.is(), "can't get Tables" );
862 if (!xTables
.is()) return SCERR_EXPORT_CONNECT
;
864 uno::Reference
<sdbcx::XDataDescriptorFactory
> xTablesFact( xTables
, uno::UNO_QUERY
);
865 OSL_ENSURE( xTablesFact
.is(), "can't get tables factory" );
866 if (!xTablesFact
.is()) return SCERR_EXPORT_CONNECT
;
868 uno::Reference
<sdbcx::XAppend
> xTablesAppend( xTables
, uno::UNO_QUERY
);
869 OSL_ENSURE( xTablesAppend
.is(), "can't get tables XAppend" );
870 if (!xTablesAppend
.is()) return SCERR_EXPORT_CONNECT
;
872 uno::Reference
<beans::XPropertySet
> xTableDesc
= xTablesFact
->createDataDescriptor();
873 OSL_ENSURE( xTableDesc
.is(), "can't get table descriptor" );
874 if (!xTableDesc
.is()) return SCERR_EXPORT_CONNECT
;
876 aAny
<<= OUString( aTabName
);
877 xTableDesc
->setPropertyValue( OUString(SC_DBPROP_NAME
), aAny
);
881 uno::Reference
<sdbcx::XColumnsSupplier
> xColumnsSupp( xTableDesc
, uno::UNO_QUERY
);
882 OSL_ENSURE( xColumnsSupp
.is(), "can't get columns supplier" );
883 if (!xColumnsSupp
.is()) return SCERR_EXPORT_CONNECT
;
885 uno::Reference
<container::XNameAccess
> xColumns
= xColumnsSupp
->getColumns();
886 OSL_ENSURE( xColumns
.is(), "can't get columns" );
887 if (!xColumns
.is()) return SCERR_EXPORT_CONNECT
;
889 uno::Reference
<sdbcx::XDataDescriptorFactory
> xColumnsFact( xColumns
, uno::UNO_QUERY
);
890 OSL_ENSURE( xColumnsFact
.is(), "can't get columns factory" );
891 if (!xColumnsFact
.is()) return SCERR_EXPORT_CONNECT
;
893 uno::Reference
<sdbcx::XAppend
> xColumnsAppend( xColumns
, uno::UNO_QUERY
);
894 OSL_ENSURE( xColumnsAppend
.is(), "can't get columns XAppend" );
895 if (!xColumnsAppend
.is()) return SCERR_EXPORT_CONNECT
;
897 const OUString
* pColNames
= aColNames
.getConstArray();
898 const sal_Int32
* pColTypes
= aColTypes
.getConstArray();
899 const sal_Int32
* pColLengths
= aColLengths
.getConstArray();
900 const sal_Int32
* pColScales
= aColScales
.getConstArray();
903 for (nCol
=0; nCol
<nColCount
; nCol
++)
905 uno::Reference
<beans::XPropertySet
> xColumnDesc
= xColumnsFact
->createDataDescriptor();
906 OSL_ENSURE( xColumnDesc
.is(), "can't get column descriptor" );
907 if (!xColumnDesc
.is()) return SCERR_EXPORT_CONNECT
;
909 aAny
<<= pColNames
[nCol
];
910 xColumnDesc
->setPropertyValue( OUString(SC_DBPROP_NAME
), aAny
);
912 aAny
<<= pColTypes
[nCol
];
913 xColumnDesc
->setPropertyValue( OUString(SC_DBPROP_TYPE
), aAny
);
915 aAny
<<= pColLengths
[nCol
];
916 xColumnDesc
->setPropertyValue( OUString(SC_DBPROP_PRECISION
), aAny
);
918 aAny
<<= pColScales
[nCol
];
919 xColumnDesc
->setPropertyValue( OUString(SC_DBPROP_SCALE
), aAny
);
921 xColumnsAppend
->appendByDescriptor( xColumnDesc
);
924 xTablesAppend
->appendByDescriptor( xTableDesc
);
926 // get row set for writing
927 uno::Reference
<lang::XMultiServiceFactory
> xFactory
= comphelper::getProcessServiceFactory();
928 uno::Reference
<sdbc::XRowSet
> xRowSet( xFactory
->createInstance(
929 OUString( SC_SERVICE_ROWSET
) ),
931 ::utl::DisposableComponent
aRowSetHelper(xRowSet
);
932 uno::Reference
<beans::XPropertySet
> xRowProp( xRowSet
, uno::UNO_QUERY
);
933 OSL_ENSURE( xRowProp
.is(), "can't get RowSet" );
934 if (!xRowProp
.is()) return SCERR_EXPORT_CONNECT
;
936 aAny
<<= xConnection
;
937 xRowProp
->setPropertyValue( OUString(SC_DBPROP_ACTIVECONNECTION
), aAny
);
939 aAny
<<= (sal_Int32
) sdb::CommandType::TABLE
;
940 xRowProp
->setPropertyValue( OUString(SC_DBPROP_COMMANDTYPE
), aAny
);
942 aAny
<<= OUString( aTabName
);
943 xRowProp
->setPropertyValue( OUString(SC_DBPROP_COMMAND
), aAny
);
949 uno::Reference
<sdbc::XResultSetUpdate
> xResultUpdate( xRowSet
, uno::UNO_QUERY
);
950 OSL_ENSURE( xResultUpdate
.is(), "can't get XResultSetUpdate" );
951 if (!xResultUpdate
.is()) return SCERR_EXPORT_CONNECT
;
953 uno::Reference
<sdbc::XRowUpdate
> xRowUpdate( xRowSet
, uno::UNO_QUERY
);
954 OSL_ENSURE( xRowUpdate
.is(), "can't get XRowUpdate" );
955 if (!xRowUpdate
.is()) return SCERR_EXPORT_CONNECT
;
957 SCROW nFirstDataRow
= ( bHasFieldNames
? nFirstRow
+ 1 : nFirstRow
);
960 for ( nDocRow
= nFirstDataRow
; nDocRow
<= nLastRow
; nDocRow
++ )
962 xResultUpdate
->moveToInsertRow();
964 for (nCol
=0; nCol
<nColCount
; nCol
++)
966 SCCOL nDocCol
= sal::static_int_cast
<SCCOL
>( nFirstCol
+ nCol
);
968 switch (pColTypes
[nCol
])
970 case sdbc::DataType::LONGVARCHAR
:
972 ScRefCellValue aCell
;
973 aCell
.assign(aDocument
, ScAddress(nDocCol
, nDocRow
, nTab
));
974 if (!aCell
.isEmpty())
976 if (aCell
.meType
== CELLTYPE_EDIT
)
977 { // Paragraphs erhalten
978 lcl_getLongVarCharEditString(aString
, aCell
, aEditEngine
);
982 lcl_getLongVarCharString(
983 aString
, aDocument
, nDocCol
, nDocRow
, nTab
, *pNumFmt
);
985 xRowUpdate
->updateString( nCol
+1, aString
);
988 xRowUpdate
->updateNull( nCol
+1 );
992 case sdbc::DataType::VARCHAR
:
993 aString
= aDocument
.GetString(nDocCol
, nDocRow
, nTab
);
994 xRowUpdate
->updateString( nCol
+1, aString
);
995 if ( nErr
== eERR_OK
&& pColLengths
[nCol
] < aString
.getLength() )
996 nErr
= SCWARN_EXPORT_DATALOST
;
999 case sdbc::DataType::DATE
:
1001 aDocument
.GetValue( nDocCol
, nDocRow
, nTab
, fVal
);
1002 // zwischen 0 Wert und 0 kein Wert unterscheiden
1003 bool bIsNull
= (fVal
== 0.0);
1005 bIsNull
= !aDocument
.HasValueData( nDocCol
, nDocRow
, nTab
);
1008 xRowUpdate
->updateNull( nCol
+1 );
1009 if ( nErr
== eERR_OK
&&
1010 aDocument
.HasStringData( nDocCol
, nDocRow
, nTab
) )
1011 nErr
= SCWARN_EXPORT_DATALOST
;
1015 Date aDate
= *(pNumFmt
->GetNullDate()); // tools date
1016 aDate
+= (long)fVal
; //! approxfloor?
1017 xRowUpdate
->updateDate( nCol
+1, aDate
.GetUNODate() );
1022 case sdbc::DataType::DECIMAL
:
1023 case sdbc::DataType::BIT
:
1024 aDocument
.GetValue( nDocCol
, nDocRow
, nTab
, fVal
);
1025 if ( fVal
== 0.0 && nErr
== eERR_OK
&&
1026 aDocument
.HasStringData( nDocCol
, nDocRow
, nTab
) )
1027 nErr
= SCWARN_EXPORT_DATALOST
;
1028 if ( pColTypes
[nCol
] == sdbc::DataType::BIT
)
1029 xRowUpdate
->updateBoolean( nCol
+1, ( fVal
!= 0.0 ) );
1031 xRowUpdate
->updateDouble( nCol
+1, fVal
);
1035 OSL_FAIL( "ScDocShell::DBaseExport: unknown FieldType" );
1036 if ( nErr
== eERR_OK
)
1037 nErr
= SCWARN_EXPORT_DATALOST
;
1038 aDocument
.GetValue( nDocCol
, nDocRow
, nTab
, fVal
);
1039 xRowUpdate
->updateDouble( nCol
+1, fVal
);
1043 xResultUpdate
->insertRow();
1045 //! error handling and recovery of old
1046 //! ScDocShell::SbaSdbExport is still missing!
1048 if ( !aProgress
.SetStateOnPercent( nDocRow
- nFirstRow
) )
1050 nErr
= SCERR_EXPORT_DATA
;
1055 comphelper::disposeComponent( xRowSet
);
1056 comphelper::disposeComponent( xConnection
);
1058 catch ( const sdbc::SQLException
& aException
)
1060 sal_Int32 nError
= aException
.ErrorCode
;
1061 #if OSL_DEBUG_LEVEL > 1
1062 fprintf( stderr
, "ScDocShell::DBaseExport: SQLException ErrorCode: %d, SQLState: %s, Message: %s\n",
1063 (int)nError
, OUStringToOString( aException
.SQLState
,
1064 RTL_TEXTENCODING_UTF8
).getStr(), OUStringToOString(
1065 aException
.Message
, RTL_TEXTENCODING_UTF8
).getStr());
1067 if (nError
== 22018 || nError
== 22001)
1069 // SQL error 22018: Character not in target encoding.
1070 // SQL error 22001: String length exceeds field width (after encoding).
1071 bool bEncErr
= (nError
== 22018);
1072 bool bIsOctetTextEncoding
= rtl_isOctetTextEncoding( eCharSet
);
1073 OSL_ENSURE( !bEncErr
|| bIsOctetTextEncoding
, "ScDocShell::DBaseExport: encoding error and not an octect textencoding");
1074 SCCOL nDocCol
= nFirstCol
;
1075 const sal_Int32
* pColTypes
= aColTypes
.getConstArray();
1076 const sal_Int32
* pColLengths
= aColLengths
.getConstArray();
1077 ScHorizontalCellIterator
aIter( &aDocument
, nTab
, nFirstCol
,
1078 nDocRow
, nLastCol
, nDocRow
);
1079 ScRefCellValue
* pCell
= NULL
;
1081 while (bTest
&& ((pCell
= aIter
.GetNext( nDocCol
, nDocRow
)) != NULL
))
1083 SCCOL nCol
= nDocCol
- nFirstCol
;
1084 switch (pColTypes
[nCol
])
1086 case sdbc::DataType::LONGVARCHAR
:
1088 if (pCell
->meType
== CELLTYPE_EDIT
)
1089 lcl_getLongVarCharEditString(aString
, *pCell
, aEditEngine
);
1091 lcl_getLongVarCharString(
1092 aString
, aDocument
, nDocCol
, nDocRow
, nTab
, *pNumFmt
);
1096 case sdbc::DataType::VARCHAR
:
1097 aString
= aDocument
.GetString(nDocCol
, nDocRow
, nTab
);
1100 // NOTE: length of DECIMAL fields doesn't need to be
1101 // checked here, the database driver adjusts the field
1102 // width accordingly.
1110 if (bIsOctetTextEncoding
)
1112 OUString
aOUString( aString
);
1114 if (!aOUString
.convertToString( &aOString
, eCharSet
,
1115 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
|
1116 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
))
1121 nLen
= aOString
.getLength();
1122 #if OSL_DEBUG_LEVEL > 1
1124 fprintf( stderr
, "ScDocShell::DBaseExport encoding error, string with default replacements: ``%s''\n",
1125 OUStringToOString( aOUString
, eCharSet
).getStr());
1129 nLen
= aString
.getLength() * sizeof(sal_Unicode
);
1131 pColTypes
[nCol
] != sdbc::DataType::LONGVARCHAR
&&
1132 pColLengths
[nCol
] < nLen
)
1135 #if OSL_DEBUG_LEVEL > 1
1136 fprintf( stderr
, "ScDocShell::DBaseExport: field width: %d, encoded length: %d\n",
1137 (int)pColLengths
[nCol
], (int)nLen
);
1144 OUString
sPosition( ScAddress( nDocCol
, nDocRow
, nTab
).GetColRowString());
1145 OUString
sEncoding( SvxTextEncodingTable().GetTextString( eCharSet
));
1146 nErr
= *new TwoStringErrorInfo( (bEncErr
? SCERR_EXPORT_ENCODING
:
1147 SCERR_EXPORT_FIELDWIDTH
), sPosition
, sEncoding
,
1148 ERRCODE_BUTTON_OK
| ERRCODE_MSG_ERROR
);
1150 else if ( !aException
.Message
.isEmpty() )
1151 nErr
= *new StringErrorInfo( (SCERR_EXPORT_SQLEXCEPTION
), aException
.Message
, ERRCODE_BUTTON_OK
| ERRCODE_MSG_ERROR
);
1153 nErr
= SCERR_EXPORT_DATA
;
1155 catch ( uno::Exception
& )
1157 OSL_FAIL("Unexpected exception in database");
1158 nErr
= ERRCODE_IO_GENERAL
;
1162 #endif // HAVE_FEATURE_DBCONNECTIVITY
1165 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */