/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #ifndef DISABLE_DBCONNECTIVITY #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scerrors.hxx" #include "docsh.hxx" #include "filter.hxx" #include "progress.hxx" #include "formulacell.hxx" #include "editutil.hxx" #include "cellform.hxx" #include "dbdocutl.hxx" #include "dociter.hxx" #include "globstr.hrc" #include "svl/zformat.hxx" #include "svl/intitem.hxx" #include "patattr.hxx" #include "scitems.hxx" #include "docpool.hxx" #include "segmenttree.hxx" #include "docparam.hxx" #include "cellvalue.hxx" #include #include using namespace com::sun::star; using ::std::vector; // ----------------------------------------------------------------------- #define SC_SERVICE_ROWSET "com.sun.star.sdb.RowSet" //! move to a header file? #define SC_DBPROP_ACTIVECONNECTION "ActiveConnection" #define SC_DBPROP_COMMAND "Command" #define SC_DBPROP_COMMANDTYPE "CommandType" #define SC_DBPROP_PROPCHANGE_NOTIFY "PropertyChangeNotificationEnabled" #define SC_DBPROP_NAME "Name" #define SC_DBPROP_TYPE "Type" #define SC_DBPROP_PRECISION "Precision" #define SC_DBPROP_SCALE "Scale" #define SC_DBPROP_EXTENSION "Extension" #define SC_DBPROP_CHARSET "CharSet" #ifndef DISABLE_DBCONNECTIVITY namespace { sal_uLong lcl_getDBaseConnection(uno::Reference& _rDrvMgr, uno::Reference& _rConnection, OUString& _rTabName, const OUString& rFullFileName, rtl_TextEncoding eCharSet) { INetURLObject aURL; aURL.SetSmartProtocol( INET_PROT_FILE ); aURL.SetSmartURL( rFullFileName ); _rTabName = aURL.getBase( INetURLObject::LAST_SEGMENT, true, INetURLObject::DECODE_UNAMBIGUOUS ); OUString aExtension = aURL.getExtension(); aURL.removeSegment(); aURL.removeFinalSlash(); OUString aPath = aURL.GetMainURL(INetURLObject::NO_DECODE); uno::Reference xContext = comphelper::getProcessComponentContext(); _rDrvMgr.set( sdbc::DriverManager::create( xContext ) ); // get connection OUString aConnUrl("sdbc:dbase:"); aConnUrl += aPath; svxform::ODataAccessCharsetHelper aHelper; ::std::vector< rtl_TextEncoding > aEncodings; aHelper.getSupportedTextEncodings( aEncodings ); ::std::vector< rtl_TextEncoding >::iterator aIter = ::std::find(aEncodings.begin(),aEncodings.end(),(rtl_TextEncoding) eCharSet); if ( aIter == aEncodings.end() ) { OSL_FAIL( "DBaseImport: dbtools::OCharsetMap doesn't know text encoding" ); return SCERR_IMPORT_CONNECT; } // if ( aIter == aMap.end() ) OUString aCharSetStr; if ( RTL_TEXTENCODING_DONTKNOW != *aIter ) { // it's not the virtual "system charset" const char* pIanaName = rtl_getMimeCharsetFromTextEncoding( *aIter ); OSL_ENSURE( pIanaName, "invalid mime name!" ); if ( pIanaName ) aCharSetStr = OUString::createFromAscii( pIanaName ); } uno::Sequence aProps(2); aProps[0].Name = SC_DBPROP_EXTENSION; aProps[0].Value <<= OUString( aExtension ); aProps[1].Name = SC_DBPROP_CHARSET; aProps[1].Value <<= aCharSetStr; _rConnection = _rDrvMgr->getConnectionWithInfo( aConnUrl, aProps ); return 0L; } } #endif // !DISABLE_DBCONNECTIVITY // ----------------------------------------------------------------------- // MoveFile/KillFile/IsDocument: similar to SfxContentHelper bool ScDocShell::MoveFile( const INetURLObject& rSourceObj, const INetURLObject& rDestObj ) { bool bMoveData = true; bool bRet = true, bKillSource = false; if ( rSourceObj.GetProtocol() != rDestObj.GetProtocol() ) { bMoveData = false; bKillSource = true; } OUString aName = rDestObj.getName(); INetURLObject aDestPathObj = rDestObj; aDestPathObj.removeSegment(); aDestPathObj.setFinalSlash(); try { ::ucbhelper::Content aDestPath( aDestPathObj.GetMainURL(INetURLObject::NO_DECODE), uno::Reference< ::com::sun::star::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); uno::Reference< ::com::sun::star::ucb::XCommandInfo > xInfo = aDestPath.getCommands(); OUString aTransferName = "transfer"; if ( xInfo->hasCommandByName( aTransferName ) ) { aDestPath.executeCommand( aTransferName, uno::makeAny( ::com::sun::star::ucb::TransferInfo( bMoveData, rSourceObj.GetMainURL(INetURLObject::NO_DECODE), aName, ::com::sun::star::ucb::NameClash::ERROR ) ) ); } else { OSL_FAIL( "transfer command not available" ); } } catch( uno::Exception& ) { // ucb may throw different exceptions on failure now bRet = false; } if ( bKillSource ) KillFile( rSourceObj ); return bRet; } bool ScDocShell::KillFile( const INetURLObject& rURL ) { bool bRet = true; try { ::ucbhelper::Content aCnt( rURL.GetMainURL(INetURLObject::NO_DECODE), uno::Reference< ::com::sun::star::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); aCnt.executeCommand( OUString( "delete" ), comphelper::makeBoolAny( sal_True ) ); } catch( uno::Exception& ) { // ucb may throw different exceptions on failure now bRet = false; } return bRet; } bool ScDocShell::IsDocument( const INetURLObject& rURL ) { bool bRet = false; try { ::ucbhelper::Content aCnt( rURL.GetMainURL(INetURLObject::NO_DECODE), uno::Reference< ::com::sun::star::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); bRet = aCnt.isDocument(); } catch( uno::Exception& ) { // ucb may throw different exceptions on failure now - warning only OSL_FAIL( "Any other exception" ); } return bRet; } // ----------------------------------------------------------------------- #ifndef DISABLE_DBCONNECTIVITY static void lcl_setScalesToColumns(ScDocument& rDoc, const vector& rScales) { SvNumberFormatter* pFormatter = rDoc.GetFormatTable(); if (!pFormatter) return; SCCOL nColCount = static_cast(rScales.size()); for (SCCOL i = 0; i < nColCount; ++i) { if (rScales[i] < 0) continue; sal_uInt32 nOldFormat; rDoc.GetNumberFormat(static_cast(i), 0, 0, nOldFormat); const SvNumberformat* pOldEntry = pFormatter->GetEntry(nOldFormat); if (!pOldEntry) continue; LanguageType eLang = pOldEntry->GetLanguage(); bool bThousand, bNegRed; sal_uInt16 nPrecision, nLeading; pOldEntry->GetFormatSpecialInfo(bThousand, bNegRed, nPrecision, nLeading); nPrecision = static_cast(rScales[i]); OUString aNewPicture = pFormatter->GenerateFormat(nOldFormat, eLang, bThousand, bNegRed, nPrecision, nLeading); sal_uInt32 nNewFormat = pFormatter->GetEntryKey(aNewPicture, eLang); if (nNewFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) { sal_Int32 nErrPos = 0; short nNewType = 0; bool bOk = pFormatter->PutEntry( aNewPicture, nErrPos, nNewType, nNewFormat, eLang); if (!bOk) continue; } ScPatternAttr aNewAttrs( rDoc.GetPool() ); SfxItemSet& rSet = aNewAttrs.GetItemSet(); rSet.Put( SfxUInt32Item(ATTR_VALUE_FORMAT, nNewFormat) ); rDoc.ApplyPatternAreaTab(static_cast(i), 0, static_cast(i), MAXROW, 0, aNewAttrs); } } #endif // !DISABLE_DBCONNECTIVITY sal_uLong ScDocShell::DBaseImport( const OUString& rFullFileName, rtl_TextEncoding eCharSet, ScColWidthParam aColWidthParam[MAXCOLCOUNT], ScFlatBoolRowSegments& rRowHeightsRecalc ) { #ifdef DISABLE_DBCONNECTIVITY (void) rFullFileName; (void) eCharSet; (void) aColWidthParam; (void) rRowHeightsRecalc; return ERRCODE_IO_GENERAL; #else sal_uLong nErr = eERR_OK; long i; long nColCount = 0; // Try to get the Text Encoding from the driver if( eCharSet == RTL_TEXTENCODING_IBM_850 ) eCharSet = RTL_TEXTENCODING_DONTKNOW; try { OUString aTabName; uno::Reference xDrvMan; uno::Reference xConnection; sal_uLong nRet = lcl_getDBaseConnection(xDrvMan,xConnection,aTabName,rFullFileName,eCharSet); if ( !xConnection.is() || !xDrvMan.is() ) return nRet; ::utl::DisposableComponent aConnectionHelper(xConnection); ScProgress aProgress( this, ScGlobal::GetRscString( STR_LOAD_DOC ), 0 ); uno::Reference xFactory = comphelper::getProcessServiceFactory(); uno::Reference xRowSet( xFactory->createInstance( OUString( SC_SERVICE_ROWSET ) ), uno::UNO_QUERY); ::utl::DisposableComponent aRowSetHelper(xRowSet); uno::Reference xRowProp( xRowSet, uno::UNO_QUERY ); OSL_ENSURE( xRowProp.is(), "can't get RowSet" ); if (!xRowProp.is()) return SCERR_IMPORT_CONNECT; sal_Int32 nType = sdb::CommandType::TABLE; uno::Any aAny; aAny <<= xConnection; xRowProp->setPropertyValue( OUString(SC_DBPROP_ACTIVECONNECTION), aAny ); aAny <<= nType; xRowProp->setPropertyValue( OUString(SC_DBPROP_COMMANDTYPE), aAny ); aAny <<= OUString( aTabName ); xRowProp->setPropertyValue( OUString(SC_DBPROP_COMMAND), aAny ); aAny <<= false; xRowProp->setPropertyValue( OUString(SC_DBPROP_PROPCHANGE_NOTIFY), aAny ); xRowSet->execute(); uno::Reference xMeta; uno::Reference xMetaSupp( xRowSet, uno::UNO_QUERY ); if ( xMetaSupp.is() ) xMeta = xMetaSupp->getMetaData(); if ( xMeta.is() ) nColCount = xMeta->getColumnCount(); // this is the number of real columns if ( nColCount > MAXCOL+1 ) { nColCount = MAXCOL+1; nErr = SCWARN_IMPORT_COLUMN_OVERFLOW; // warning } uno::Reference xRow( xRowSet, uno::UNO_QUERY ); OSL_ENSURE( xRow.is(), "can't get Row" ); if (!xRow.is()) return SCERR_IMPORT_CONNECT; // currency flag is not needed for dBase uno::Sequence aColTypes( nColCount ); // column types sal_Int32* pTypeArr = aColTypes.getArray(); for (i=0; igetColumnType( i+1 ); // read column names //! add type descriptions aProgress.SetState( 0 ); vector aScales(nColCount, -1); for (i=0; igetColumnLabel( i+1 ); switch ( pTypeArr[i] ) { case sdbc::DataType::BIT: aHeader += ",L"; break; case sdbc::DataType::DATE: aHeader += ",D"; break; case sdbc::DataType::LONGVARCHAR: aHeader += ",M"; break; case sdbc::DataType::VARCHAR: aHeader += ",C," + OUString::number( xMeta->getColumnDisplaySize( i+1 ) ); break; case sdbc::DataType::DECIMAL: { long nPrec = xMeta->getPrecision( i+1 ); long nScale = xMeta->getScale( i+1 ); aHeader += ",N," + OUString::number( SvDbaseConverter::ConvertPrecisionToDbase( nPrec, nScale ) ) + "," + OUString::number( nScale ); aScales[i] = nScale; } break; } aDocument.SetString( static_cast(i), 0, 0, aHeader ); } lcl_setScalesToColumns(aDocument, aScales); SCROW nRow = 1; // 0 is column titles sal_Bool bEnd = false; while ( !bEnd && xRowSet->next() ) { if ( nRow <= MAXROW ) { bool bSimpleRow = true; SCCOL nCol = 0; for (i=0; i aColWidthParam[nCol].mnMaxTextLen) { aColWidthParam[nCol].mnMaxTextLen = aStrData.mnStrLength; aColWidthParam[nCol].mnMaxTextRow = nRow; } if (!aStrData.mbSimpleText) { bSimpleRow = false; aColWidthParam[nCol].mbSimpleText = false; } ++nCol; } if (!bSimpleRow) rRowHeightsRecalc.setTrue(nRow, nRow); ++nRow; } else // past the end of the spreadsheet { bEnd = sal_True; // don't continue nErr = SCWARN_IMPORT_RANGE_OVERFLOW; // warning message } } } catch ( sdbc::SQLException& ) { nErr = SCERR_IMPORT_CONNECT; } catch ( uno::Exception& ) { OSL_FAIL("Unexpected exception in database"); nErr = ERRCODE_IO_GENERAL; } return nErr; #endif // !DISABLE_DBCONNECTIVITY } #ifndef DISABLE_DBCONNECTIVITY namespace { inline bool IsAsciiDigit( sal_Unicode c ) { return 0x30 <= c && c <= 0x39; } inline bool IsAsciiAlpha( sal_Unicode c ) { return (0x41 <= c && c <= 0x5a) || (0x61 <= c && c <= 0x7a); } void lcl_GetColumnTypes( ScDocShell& rDocShell, const ScRange& rDataRange, bool bHasFieldNames, OUString* pColNames, sal_Int32* pColTypes, sal_Int32* pColLengths, sal_Int32* pColScales, bool& bHasMemo, rtl_TextEncoding eCharSet ) { // updating of column titles didn't work in 5.2 and isn't always wanted // (saving normally shouldn't modify the document) //! read flag from configuration bool bUpdateTitles = false; ScDocument* pDoc = rDocShell.GetDocument(); SvNumberFormatter* pNumFmt = pDoc->GetFormatTable(); SCTAB nTab = rDataRange.aStart.Tab(); SCCOL nFirstCol = rDataRange.aStart.Col(); SCROW nFirstRow = rDataRange.aStart.Row(); SCCOL nLastCol = rDataRange.aEnd.Col(); SCROW nLastRow = rDataRange.aEnd.Row(); typedef boost::unordered_set StrSetType; StrSetType aFieldNames; long nField = 0; SCROW nFirstDataRow = ( bHasFieldNames ? nFirstRow + 1 : nFirstRow ); for ( SCCOL nCol = nFirstCol; nCol <= nLastCol; nCol++ ) { bool bTypeDefined = false; bool bPrecDefined = false; sal_Int32 nFieldLen = 0; sal_Int32 nPrecision = 0; sal_Int32 nDbType = sdbc::DataType::SQLNULL; OUString aFieldName; OUString aString; // Feldname[,Type[,Width[,Prec]]] // Typ etc.: L; D; C[,W]; N[,W[,P]] if ( bHasFieldNames ) { aString = pDoc->GetString(nCol, nFirstRow, nTab); aString = aString.toAsciiUpperCase(); sal_Int32 nToken = comphelper::string::getTokenCount(aString, ','); if ( nToken > 1 ) { aFieldName = aString.getToken( 0, ',' ); aString = comphelper::string::remove(aString, ' '); switch ( aString.getToken( 1, ',' )[0] ) { case 'L' : nDbType = sdbc::DataType::BIT; nFieldLen = 1; bTypeDefined = true; bPrecDefined = true; break; case 'D' : nDbType = sdbc::DataType::DATE; nFieldLen = 8; bTypeDefined = true; bPrecDefined = true; break; case 'M' : nDbType = sdbc::DataType::LONGVARCHAR; nFieldLen = 10; bTypeDefined = true; bPrecDefined = true; bHasMemo = true; break; case 'C' : nDbType = sdbc::DataType::VARCHAR; bTypeDefined = true; bPrecDefined = true; break; case 'N' : nDbType = sdbc::DataType::DECIMAL; break; } if ( bTypeDefined && !nFieldLen && nToken > 2 ) { nFieldLen = aString.getToken( 2, ',' ).toInt32(); if ( !bPrecDefined && nToken > 3 ) { OUString aTmp( aString.getToken( 3, ',' ) ); if ( CharClass::isAsciiNumeric(aTmp) ) { nPrecision = aTmp.toInt32(); bPrecDefined = true; } } } } else aFieldName = aString; // Feldnamen pruefen und ggbf. gueltigen Feldnamen erzeugen. // Erstes Zeichen muss Buchstabe sein, // weitere nur alphanumerisch und Unterstrich erlaubt, // "_DBASELOCK" ist reserviert (obsolet weil erstes Zeichen kein Buchstabe), // keine doppelten Namen. if ( !IsAsciiAlpha( aFieldName[0] ) ) aFieldName = "N" + aFieldName; OUString aTmpStr; sal_Unicode c; for ( const sal_Unicode* p = aFieldName.getStr(); ( c = *p ) != 0; p++ ) { if ( IsAsciiAlpha( c ) || IsAsciiDigit( c ) || c == '_' ) aTmpStr += OUString(c); else aTmpStr += "_"; } aFieldName = aTmpStr; if ( aFieldName.getLength() > 10 ) aFieldName = aFieldName.copy(0, 10); if (!aFieldNames.insert(aFieldName).second) { // doppelter Feldname, numerisch erweitern sal_uInt16 nSub = 1; OUString aFixPart( aFieldName ); do { ++nSub; OUString aVarPart = OUString::number( nSub ); if ( aFixPart.getLength() + aVarPart.getLength() > 10 ) aFixPart = aFixPart.copy( 0, 10 - aVarPart.getLength() ); aFieldName = aFixPart; aFieldName += aVarPart; } while (!aFieldNames.insert(aFieldName).second); } } else { aFieldName = "N" + OUString::number(nCol+1); } if ( !bTypeDefined ) { // Feldtyp ScRefCellValue aCell; aCell.assign(*pDoc, ScAddress(nCol, nFirstDataRow, nTab)); if (aCell.isEmpty() || aCell.hasString()) nDbType = sdbc::DataType::VARCHAR; else { sal_uInt32 nFormat; pDoc->GetNumberFormat( nCol, nFirstDataRow, nTab, nFormat ); switch ( pNumFmt->GetType( nFormat ) ) { case NUMBERFORMAT_LOGICAL : nDbType = sdbc::DataType::BIT; nFieldLen = 1; break; case NUMBERFORMAT_DATE : nDbType = sdbc::DataType::DATE; nFieldLen = 8; break; case NUMBERFORMAT_TIME : case NUMBERFORMAT_DATETIME : nDbType = sdbc::DataType::VARCHAR; break; default: nDbType = sdbc::DataType::DECIMAL; } } } bool bSdbLenAdjusted = false; bool bSdbLenBad = false; // Feldlaenge if ( nDbType == sdbc::DataType::VARCHAR && !nFieldLen ) { // maximale Feldbreite bestimmen nFieldLen = pDoc->GetMaxStringLen( nTab, nCol, nFirstDataRow, nLastRow, eCharSet ); if ( nFieldLen == 0 ) nFieldLen = 1; } else if ( nDbType == sdbc::DataType::DECIMAL ) { // maximale Feldbreite und Nachkommastellen bestimmen xub_StrLen nLen; sal_uInt16 nPrec; nLen = pDoc->GetMaxNumberStringLen( nPrec, nTab, nCol, nFirstDataRow, nLastRow ); // dBaseIII Limit Nachkommastellen: 15 if ( nPrecision > 15 ) nPrecision = 15; if ( nPrec > 15 ) nPrec = 15; if ( bPrecDefined && nPrecision != nPrec ) { // Laenge auf vorgegebene Nachkommastellen anpassen if ( nPrecision ) nLen = sal::static_int_cast( nLen + ( nPrecision - nPrec ) ); else nLen -= nPrec+1; // auch den . mit raus } if ( nLen > nFieldLen && !bTypeDefined ) nFieldLen = nLen; if ( !bPrecDefined ) nPrecision = nPrec; if ( nFieldLen == 0 ) nFieldLen = 1; else if ( nFieldLen > 19 ) nFieldLen = 19; // dBaseIII Limit Feldlaenge numerisch: 19 if ( nPrecision && nFieldLen < nPrecision + 2 ) nFieldLen = nPrecision + 2; // 0. muss mit reinpassen // 538 MUST: Sdb internal representation adds 2 to the field length! // To give the user what he wants we must substract it here. //! CAVEAT! There is no way to define a numeric field with a length //! of 1 and no decimals! if ( nFieldLen == 1 && nPrecision == 0 ) bSdbLenBad = true; nFieldLen = SvDbaseConverter::ConvertPrecisionToOdbc( nFieldLen, nPrecision ); bSdbLenAdjusted = true; } if ( nFieldLen > 254 ) { if ( nDbType == sdbc::DataType::VARCHAR ) { // zu lang fuer normales Textfeld => Memofeld nDbType = sdbc::DataType::LONGVARCHAR; nFieldLen = 10; bHasMemo = true; } else nFieldLen = 254; // dumm gelaufen.. } pColNames[nField] = aFieldName; pColTypes[nField] = nDbType; pColLengths[nField] = nFieldLen; pColScales[nField] = nPrecision; // undo change to field length, reflect reality if ( bSdbLenAdjusted ) { nFieldLen = SvDbaseConverter::ConvertPrecisionToDbase( nFieldLen, nPrecision ); if ( bSdbLenBad && nFieldLen == 1 ) nFieldLen = 2; // THIS is reality } if ( bUpdateTitles ) { // Angabe anpassen und ausgeben OUString aOutString = aFieldName; switch ( nDbType ) { case sdbc::DataType::BIT : aOutString += ",L"; break; case sdbc::DataType::DATE : aOutString += ",D"; break; case sdbc::DataType::LONGVARCHAR : aOutString += ",M"; break; case sdbc::DataType::VARCHAR : aOutString += ",C," + OUString::number( nFieldLen ); break; case sdbc::DataType::DECIMAL : aOutString += ",N," + OUString::number( nFieldLen ) + "," + OUString::number( nPrecision ); break; } if ( !aOutString.equalsIgnoreAsciiCase( aString ) ) { pDoc->SetString( nCol, nFirstRow, nTab, aOutString ); rDocShell.PostPaint( nCol, nFirstRow, nTab, nCol, nFirstRow, nTab, PAINT_GRID ); } } ++nField; } } inline void lcl_getLongVarCharEditString( OUString& rString, const ScRefCellValue& rCell, ScFieldEditEngine& rEditEngine ) { if (!rCell.mpEditText) return; rEditEngine.SetText(*rCell.mpEditText); rString = rEditEngine.GetText( LINEEND_CRLF ); } inline void lcl_getLongVarCharString( OUString& rString, ScDocument& rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, SvNumberFormatter& rNumFmt ) { Color* pColor; ScAddress aPos(nCol, nRow, nTab); sal_uInt32 nFormat = rDoc.GetNumberFormat(aPos); rString = ScCellFormat::GetString(rDoc, aPos, nFormat, &pColor, rNumFmt); } } #endif // !DISABLE_DBCONNECTIVITY sal_uLong ScDocShell::DBaseExport( const OUString& rFullFileName, rtl_TextEncoding eCharSet, bool& bHasMemo ) { #ifdef DISABLE_DBCONNECTIVITY (void) rFullFileName; (void) eCharSet; (void) bHasMemo; return ERRCODE_IO_GENERAL; #else // remove the file so the dBase driver doesn't find an invalid file INetURLObject aDeleteObj( rFullFileName, INET_PROT_FILE ); KillFile( aDeleteObj ); sal_uLong nErr = eERR_OK; uno::Any aAny; SCCOL nFirstCol, nLastCol; SCROW nFirstRow, nLastRow; SCTAB nTab = GetSaveTab(); aDocument.GetDataStart( nTab, nFirstCol, nFirstRow ); aDocument.GetCellArea( nTab, nLastCol, nLastRow ); if ( nFirstCol > nLastCol ) nFirstCol = nLastCol; if ( nFirstRow > nLastRow ) nFirstRow = nLastRow; ScProgress aProgress( this, ScGlobal::GetRscString( STR_SAVE_DOC ), nLastRow - nFirstRow ); SvNumberFormatter* pNumFmt = aDocument.GetFormatTable(); sal_Bool bHasFieldNames = sal_True; for ( SCCOL nDocCol = nFirstCol; nDocCol <= nLastCol && bHasFieldNames; nDocCol++ ) { // nur Strings in erster Zeile => sind Feldnamen if ( !aDocument.HasStringData( nDocCol, nFirstRow, nTab ) ) bHasFieldNames = false; } long nColCount = nLastCol - nFirstCol + 1; uno::Sequence aColNames( nColCount ); uno::Sequence aColTypes( nColCount ); uno::Sequence aColLengths( nColCount ); uno::Sequence aColScales( nColCount ); ScRange aDataRange( nFirstCol, nFirstRow, nTab, nLastCol, nLastRow, nTab ); lcl_GetColumnTypes( *this, aDataRange, bHasFieldNames, aColNames.getArray(), aColTypes.getArray(), aColLengths.getArray(), aColScales.getArray(), bHasMemo, eCharSet ); // also needed for exception catch SCROW nDocRow = 0; ScFieldEditEngine aEditEngine(&aDocument, aDocument.GetEditPool()); OUString aString; OUString aTabName; try { uno::Reference xDrvMan; uno::Reference xConnection; sal_uLong nRet = lcl_getDBaseConnection(xDrvMan,xConnection,aTabName,rFullFileName,eCharSet); if ( !xConnection.is() || !xDrvMan.is() ) return nRet; ::utl::DisposableComponent aConnectionHelper(xConnection); // get dBase driver uno::Reference< sdbc::XDriverAccess> xAccess(xDrvMan,uno::UNO_QUERY); uno::Reference< sdbcx::XDataDefinitionSupplier > xDDSup( xAccess->getDriverByURL( xConnection->getMetaData()->getURL() ), uno::UNO_QUERY ); if ( !xDDSup.is() ) return SCERR_EXPORT_CONNECT; // create table uno::Reference xTablesSupp =xDDSup->getDataDefinitionByConnection( xConnection ); OSL_ENSURE( xTablesSupp.is(), "can't get Data Definition" ); if (!xTablesSupp.is()) return SCERR_EXPORT_CONNECT; uno::Reference xTables = xTablesSupp->getTables(); OSL_ENSURE( xTables.is(), "can't get Tables" ); if (!xTables.is()) return SCERR_EXPORT_CONNECT; uno::Reference xTablesFact( xTables, uno::UNO_QUERY ); OSL_ENSURE( xTablesFact.is(), "can't get tables factory" ); if (!xTablesFact.is()) return SCERR_EXPORT_CONNECT; uno::Reference xTablesAppend( xTables, uno::UNO_QUERY ); OSL_ENSURE( xTablesAppend.is(), "can't get tables XAppend" ); if (!xTablesAppend.is()) return SCERR_EXPORT_CONNECT; uno::Reference xTableDesc = xTablesFact->createDataDescriptor(); OSL_ENSURE( xTableDesc.is(), "can't get table descriptor" ); if (!xTableDesc.is()) return SCERR_EXPORT_CONNECT; aAny <<= OUString( aTabName ); xTableDesc->setPropertyValue( OUString(SC_DBPROP_NAME), aAny ); // create columns uno::Reference xColumnsSupp( xTableDesc, uno::UNO_QUERY ); OSL_ENSURE( xColumnsSupp.is(), "can't get columns supplier" ); if (!xColumnsSupp.is()) return SCERR_EXPORT_CONNECT; uno::Reference xColumns = xColumnsSupp->getColumns(); OSL_ENSURE( xColumns.is(), "can't get columns" ); if (!xColumns.is()) return SCERR_EXPORT_CONNECT; uno::Reference xColumnsFact( xColumns, uno::UNO_QUERY ); OSL_ENSURE( xColumnsFact.is(), "can't get columns factory" ); if (!xColumnsFact.is()) return SCERR_EXPORT_CONNECT; uno::Reference xColumnsAppend( xColumns, uno::UNO_QUERY ); OSL_ENSURE( xColumnsAppend.is(), "can't get columns XAppend" ); if (!xColumnsAppend.is()) return SCERR_EXPORT_CONNECT; const OUString* pColNames = aColNames.getConstArray(); const sal_Int32* pColTypes = aColTypes.getConstArray(); const sal_Int32* pColLengths = aColLengths.getConstArray(); const sal_Int32* pColScales = aColScales.getConstArray(); long nCol; for (nCol=0; nCol xColumnDesc = xColumnsFact->createDataDescriptor(); OSL_ENSURE( xColumnDesc.is(), "can't get column descriptor" ); if (!xColumnDesc.is()) return SCERR_EXPORT_CONNECT; aAny <<= pColNames[nCol]; xColumnDesc->setPropertyValue( OUString(SC_DBPROP_NAME), aAny ); aAny <<= pColTypes[nCol]; xColumnDesc->setPropertyValue( OUString(SC_DBPROP_TYPE), aAny ); aAny <<= pColLengths[nCol]; xColumnDesc->setPropertyValue( OUString(SC_DBPROP_PRECISION), aAny ); aAny <<= pColScales[nCol]; xColumnDesc->setPropertyValue( OUString(SC_DBPROP_SCALE), aAny ); xColumnsAppend->appendByDescriptor( xColumnDesc ); } xTablesAppend->appendByDescriptor( xTableDesc ); // get row set for writing uno::Reference xFactory = comphelper::getProcessServiceFactory(); uno::Reference xRowSet( xFactory->createInstance( OUString( SC_SERVICE_ROWSET ) ), uno::UNO_QUERY); ::utl::DisposableComponent aRowSetHelper(xRowSet); uno::Reference xRowProp( xRowSet, uno::UNO_QUERY ); OSL_ENSURE( xRowProp.is(), "can't get RowSet" ); if (!xRowProp.is()) return SCERR_EXPORT_CONNECT; aAny <<= xConnection; xRowProp->setPropertyValue( OUString(SC_DBPROP_ACTIVECONNECTION), aAny ); aAny <<= (sal_Int32) sdb::CommandType::TABLE; xRowProp->setPropertyValue( OUString(SC_DBPROP_COMMANDTYPE), aAny ); aAny <<= OUString( aTabName ); xRowProp->setPropertyValue( OUString(SC_DBPROP_COMMAND), aAny ); xRowSet->execute(); // write data rows uno::Reference xResultUpdate( xRowSet, uno::UNO_QUERY ); OSL_ENSURE( xResultUpdate.is(), "can't get XResultSetUpdate" ); if (!xResultUpdate.is()) return SCERR_EXPORT_CONNECT; uno::Reference xRowUpdate( xRowSet, uno::UNO_QUERY ); OSL_ENSURE( xRowUpdate.is(), "can't get XRowUpdate" ); if (!xRowUpdate.is()) return SCERR_EXPORT_CONNECT; SCROW nFirstDataRow = ( bHasFieldNames ? nFirstRow + 1 : nFirstRow ); double fVal; for ( nDocRow = nFirstDataRow; nDocRow <= nLastRow; nDocRow++ ) { xResultUpdate->moveToInsertRow(); for (nCol=0; nCol( nFirstCol + nCol ); switch (pColTypes[nCol]) { case sdbc::DataType::LONGVARCHAR: { ScRefCellValue aCell; aCell.assign(aDocument, ScAddress(nDocCol, nDocRow, nTab)); if (!aCell.isEmpty()) { if (aCell.meType == CELLTYPE_EDIT) { // Paragraphs erhalten lcl_getLongVarCharEditString(aString, aCell, aEditEngine); } else { lcl_getLongVarCharString( aString, aDocument, nDocCol, nDocRow, nTab, *pNumFmt); } xRowUpdate->updateString( nCol+1, aString ); } else xRowUpdate->updateNull( nCol+1 ); } break; case sdbc::DataType::VARCHAR: aString = aDocument.GetString(nDocCol, nDocRow, nTab); xRowUpdate->updateString( nCol+1, aString ); if ( nErr == eERR_OK && pColLengths[nCol] < aString.getLength() ) nErr = SCWARN_EXPORT_DATALOST; break; case sdbc::DataType::DATE: { aDocument.GetValue( nDocCol, nDocRow, nTab, fVal ); // zwischen 0 Wert und 0 kein Wert unterscheiden sal_Bool bIsNull = (fVal == 0.0); if ( bIsNull ) bIsNull = !aDocument.HasValueData( nDocCol, nDocRow, nTab ); if ( bIsNull ) { xRowUpdate->updateNull( nCol+1 ); if ( nErr == eERR_OK && aDocument.HasStringData( nDocCol, nDocRow, nTab ) ) nErr = SCWARN_EXPORT_DATALOST; } else { Date aDate = *(pNumFmt->GetNullDate()); // tools date aDate += (long)fVal; //! approxfloor? util::Date aUnoDate( aDate.GetDay(), aDate.GetMonth(), aDate.GetYear() ); xRowUpdate->updateDate( nCol+1, aUnoDate ); } } break; case sdbc::DataType::DECIMAL: case sdbc::DataType::BIT: aDocument.GetValue( nDocCol, nDocRow, nTab, fVal ); if ( fVal == 0.0 && nErr == eERR_OK && aDocument.HasStringData( nDocCol, nDocRow, nTab ) ) nErr = SCWARN_EXPORT_DATALOST; if ( pColTypes[nCol] == sdbc::DataType::BIT ) xRowUpdate->updateBoolean( nCol+1, ( fVal != 0.0 ) ); else xRowUpdate->updateDouble( nCol+1, fVal ); break; default: OSL_FAIL( "ScDocShell::DBaseExport: unknown FieldType" ); if ( nErr == eERR_OK ) nErr = SCWARN_EXPORT_DATALOST; aDocument.GetValue( nDocCol, nDocRow, nTab, fVal ); xRowUpdate->updateDouble( nCol+1, fVal ); } } xResultUpdate->insertRow(); //! error handling and recovery of old //! ScDocShell::SbaSdbExport is still missing! if ( !aProgress.SetStateOnPercent( nDocRow - nFirstRow ) ) { // UserBreak nErr = SCERR_EXPORT_DATA; break; } } comphelper::disposeComponent( xRowSet ); comphelper::disposeComponent( xConnection ); } catch ( const sdbc::SQLException& aException ) { sal_Int32 nError = aException.ErrorCode; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "ScDocShell::DBaseExport: SQLException ErrorCode: %d, SQLState: %s, Message: %s\n", (int)nError, OUStringToOString( aException.SQLState, RTL_TEXTENCODING_UTF8).getStr(), OUStringToOString( aException.Message, RTL_TEXTENCODING_UTF8).getStr()); #endif if (nError == 22018 || nError == 22001) { // SQL error 22018: Character not in target encoding. // SQL error 22001: String length exceeds field width (after encoding). bool bEncErr = (nError == 22018); bool bIsOctetTextEncoding = rtl_isOctetTextEncoding( eCharSet); OSL_ENSURE( !bEncErr || bIsOctetTextEncoding, "ScDocShell::DBaseExport: encoding error and not an octect textencoding"); SCCOL nDocCol = nFirstCol; const sal_Int32* pColTypes = aColTypes.getConstArray(); const sal_Int32* pColLengths = aColLengths.getConstArray(); ScHorizontalCellIterator aIter( &aDocument, nTab, nFirstCol, nDocRow, nLastCol, nDocRow); ScRefCellValue* pCell = NULL; bool bTest = true; while (bTest && ((pCell = aIter.GetNext( nDocCol, nDocRow)) != NULL)) { SCCOL nCol = nDocCol - nFirstCol; switch (pColTypes[nCol]) { case sdbc::DataType::LONGVARCHAR: { if (pCell->meType == CELLTYPE_EDIT) lcl_getLongVarCharEditString(aString, *pCell, aEditEngine); else lcl_getLongVarCharString( aString, aDocument, nDocCol, nDocRow, nTab, *pNumFmt); } break; case sdbc::DataType::VARCHAR: aString = aDocument.GetString(nDocCol, nDocRow, nTab); break; // NOTE: length of DECIMAL fields doesn't need to be // checked here, the database driver adjusts the field // width accordingly. default: bTest = false; } if (bTest) { sal_Int32 nLen; if (bIsOctetTextEncoding) { OUString aOUString( aString); OString aOString; if (!aOUString.convertToString( &aOString, eCharSet, RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)) { bTest = false; bEncErr = true; } nLen = aOString.getLength(); #if OSL_DEBUG_LEVEL > 1 if (!bTest) fprintf( stderr, "ScDocShell::DBaseExport encoding error, string with default replacements: ``%s''\n", OUStringToOString( aOUString, eCharSet).getStr()); #endif } else nLen = aString.getLength() * sizeof(sal_Unicode); if (!bEncErr && pColTypes[nCol] != sdbc::DataType::LONGVARCHAR && pColLengths[nCol] < nLen) { bTest = false; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "ScDocShell::DBaseExport: field width: %d, encoded length: %d\n", (int)pColLengths[nCol], (int)nLen); #endif } } else bTest = true; } OUString sPosition( ScAddress( nDocCol, nDocRow, nTab).GetColRowString()); OUString sEncoding( SvxTextEncodingTable().GetTextString( eCharSet)); nErr = *new TwoStringErrorInfo( (bEncErr ? SCERR_EXPORT_ENCODING : SCERR_EXPORT_FIELDWIDTH), sPosition, sEncoding, ERRCODE_BUTTON_OK | ERRCODE_MSG_ERROR); } else if ( !aException.Message.isEmpty() ) nErr = *new StringErrorInfo( (SCERR_EXPORT_SQLEXCEPTION), aException.Message, ERRCODE_BUTTON_OK | ERRCODE_MSG_ERROR); else nErr = SCERR_EXPORT_DATA; } catch ( uno::Exception& ) { OSL_FAIL("Unexpected exception in database"); nErr = ERRCODE_IO_GENERAL; } return nErr; #endif // !DISABLE_DBCONNECTIVITY } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */