From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- connectivity/source/drivers/calc/CCatalog.cxx | 62 ++ connectivity/source/drivers/calc/CConnection.cxx | 265 +++++++++ .../source/drivers/calc/CDatabaseMetaData.cxx | 217 +++++++ connectivity/source/drivers/calc/CDriver.cxx | 95 +++ connectivity/source/drivers/calc/CTable.cxx | 652 +++++++++++++++++++++ connectivity/source/drivers/calc/CTables.cxx | 48 ++ connectivity/source/drivers/calc/calc.component | 27 + 7 files changed, 1366 insertions(+) create mode 100644 connectivity/source/drivers/calc/CCatalog.cxx create mode 100644 connectivity/source/drivers/calc/CConnection.cxx create mode 100644 connectivity/source/drivers/calc/CDatabaseMetaData.cxx create mode 100644 connectivity/source/drivers/calc/CDriver.cxx create mode 100644 connectivity/source/drivers/calc/CTable.cxx create mode 100644 connectivity/source/drivers/calc/CTables.cxx create mode 100644 connectivity/source/drivers/calc/calc.component (limited to 'connectivity/source/drivers/calc') diff --git a/connectivity/source/drivers/calc/CCatalog.cxx b/connectivity/source/drivers/calc/CCatalog.cxx new file mode 100644 index 000000000..03c4358b9 --- /dev/null +++ b/connectivity/source/drivers/calc/CCatalog.cxx @@ -0,0 +1,62 @@ +/* -*- 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 + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; + +using namespace connectivity::calc; + +OCalcCatalog::OCalcCatalog(OCalcConnection* _pCon) + : file::OFileCatalog(_pCon) +{ +} + +void OCalcCatalog::refreshTables() +{ + ::std::vector aVector; + Sequence aTypes; + OCalcConnection::ODocHolder aDocHolder(static_cast(m_pConnection)); + Reference xResult = m_xMetaData->getTables(Any(), "%", "%", aTypes); + + if (xResult.is()) + { + Reference xRow(xResult, UNO_QUERY); + while (xResult->next()) + aVector.push_back(xRow->getString(3)); + } + if (m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset(new OCalcTables(m_xMetaData, *this, m_aMutex, aVector)); + + // this avoids that the document will be loaded a 2nd time when one table will be accessed. + //if ( m_pTables && m_pTables->hasElements() ) + // m_pTables->getByIndex(0); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CConnection.cxx b/connectivity/source/drivers/calc/CConnection.cxx new file mode 100644 index 000000000..8d4a0c01e --- /dev/null +++ b/connectivity/source/drivers/calc/CConnection.cxx @@ -0,0 +1,265 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include + +using namespace connectivity::calc; +using namespace connectivity::file; + +typedef connectivity::file::OConnection OConnection_BASE; + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::sheet; + + +OCalcConnection::OCalcConnection(ODriver* _pDriver) : OConnection(_pDriver),m_nDocCount(0) +{ + // m_aFilenameExtension is not used +} + +OCalcConnection::~OCalcConnection() +{ +} + +void OCalcConnection::construct(const OUString& url,const Sequence< PropertyValue >& info) +{ + // open file + + sal_Int32 nLen = url.indexOf(':'); + nLen = url.indexOf(':',nLen+1); + OUString aDSN(url.copy(nLen+1)); + + m_aFileName = aDSN; + INetURLObject aURL; + aURL.SetSmartProtocol(INetProtocol::File); + { + SvtPathOptions aPathOptions; + m_aFileName = aPathOptions.SubstituteVariable(m_aFileName); + } + aURL.SetSmartURL(m_aFileName); + if ( aURL.GetProtocol() == INetProtocol::NotValid ) + { + // don't pass invalid URL to loadComponentFromURL + throw SQLException(); + } + m_aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + + m_sPassword.clear(); + const char pPwd[] = "password"; + + const PropertyValue *pIter = info.getConstArray(); + const PropertyValue *pEnd = pIter + info.getLength(); + for(;pIter != pEnd;++pIter) + { + if(pIter->Name == pPwd) + { + pIter->Value >>= m_sPassword; + break; + } + } // for(;pIter != pEnd;++pIter) + ODocHolder aDocHolder(this); // just to test that the doc can be loaded + acquireDoc(); +} + +Reference< XSpreadsheetDocument> const & OCalcConnection::acquireDoc() +{ + if ( m_xDoc.is() ) + { + osl_atomic_increment(&m_nDocCount); + return m_xDoc; + } + // open read-only as long as updating isn't implemented + Sequence aArgs(m_sPassword.isEmpty() ? 2 : 3); + auto pArgs = aArgs.getArray(); + pArgs[0].Name = "Hidden"; + pArgs[0].Value <<= true; + pArgs[1].Name = "ReadOnly"; + pArgs[1].Value <<= true; + + if ( !m_sPassword.isEmpty() ) + { + pArgs[2].Name = "Password"; + pArgs[2].Value <<= m_sPassword; + } + + Reference< XDesktop2 > xDesktop = Desktop::create( getDriver()->getComponentContext() ); + Reference< XComponent > xComponent; + Any aLoaderException; + try + { + xComponent = xDesktop->loadComponentFromURL( + m_aFileName, "_blank", 0, aArgs ); + } + catch( const Exception& ) + { + aLoaderException = ::cppu::getCaughtException(); + } + + m_xDoc.set(xComponent, UNO_QUERY ); + + // if the URL is not a spreadsheet document, throw the exception here + // instead of at the first access to it + if ( !m_xDoc.is() ) + { + Any aErrorDetails; + if ( aLoaderException.hasValue() ) + { + Exception aLoaderError; + OSL_VERIFY( aLoaderException >>= aLoaderError ); + + SQLException aDetailException; + aDetailException.Message = m_aResources.getResourceStringWithSubstitution( + STR_LOAD_FILE_ERROR_MESSAGE, + "$exception_type$", aLoaderException.getValueTypeName(), + "$error_message$", aLoaderError.Message + ); + aErrorDetails <<= aDetailException; + } + + const OUString sError( m_aResources.getResourceStringWithSubstitution( + STR_COULD_NOT_LOAD_FILE, + "$filename$", m_aFileName + ) ); + ::dbtools::throwGenericSQLException( sError, *this, aErrorDetails ); + } + osl_atomic_increment(&m_nDocCount); + m_xCloseVetoButTerminateListener.set(new CloseVetoButTerminateListener); + m_xCloseVetoButTerminateListener->start(m_xDoc, xDesktop); + return m_xDoc; +} + +void OCalcConnection::releaseDoc() +{ + if ( osl_atomic_decrement(&m_nDocCount) == 0 ) + { + if (m_xCloseVetoButTerminateListener.is()) + { + m_xCloseVetoButTerminateListener->stop(); // dispose m_xDoc + m_xCloseVetoButTerminateListener.clear(); + } + m_xDoc.clear(); + } +} + +void OCalcConnection::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + m_nDocCount = 0; + if (m_xCloseVetoButTerminateListener.is()) + { + m_xCloseVetoButTerminateListener->stop(); // dispose m_xDoc + m_xCloseVetoButTerminateListener.clear(); + } + m_xDoc.clear(); + + OConnection::disposing(); +} + +// XServiceInfo + + +IMPLEMENT_SERVICE_INFO(OCalcConnection, "com.sun.star.sdbc.drivers.calc.Connection", "com.sun.star.sdbc.Connection") + + +Reference< XDatabaseMetaData > SAL_CALL OCalcConnection::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if(!xMetaData.is()) + { + xMetaData = new OCalcDatabaseMetaData(this); + m_xMetaData = xMetaData; + } + + return xMetaData; +} + + +css::uno::Reference< XTablesSupplier > OCalcConnection::createCatalog() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + Reference< XTablesSupplier > xTab = m_xCatalog; + if(!xTab.is()) + { + xTab = new OCalcCatalog(this); + m_xCatalog = xTab; + } + return xTab; +} + + +Reference< XStatement > SAL_CALL OCalcConnection::createStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + Reference< XStatement > xReturn = new connectivity::component::OComponentStatement(this); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + + +Reference< XPreparedStatement > SAL_CALL OCalcConnection::prepareStatement( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + rtl::Reference pStmt = new connectivity::component::OComponentPreparedStatement(this); + pStmt->construct(sql); + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return pStmt; +} + + +Reference< XPreparedStatement > SAL_CALL OCalcConnection::prepareCall( const OUString& /*sql*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::prepareCall", *this ); + return nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CDatabaseMetaData.cxx b/connectivity/source/drivers/calc/CDatabaseMetaData.cxx new file mode 100644 index 000000000..159349cac --- /dev/null +++ b/connectivity/source/drivers/calc/CDatabaseMetaData.cxx @@ -0,0 +1,217 @@ +/* -*- 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 +#include +#include +#include + +using namespace connectivity::calc; +using namespace connectivity::file; +using namespace connectivity::component; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::sheet; + +OCalcDatabaseMetaData::OCalcDatabaseMetaData(OConnection* _pCon) :OComponentDatabaseMetaData(_pCon) +{ +} + +OCalcDatabaseMetaData::~OCalcDatabaseMetaData() +{ +} + +OUString SAL_CALL OCalcDatabaseMetaData::getURL( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + return "sdbc:calc:" + m_pConnection->getURL(); +} + +static bool lcl_IsEmptyOrHidden( const Reference& xSheets, const OUString& rName ) +{ + Any aAny = xSheets->getByName( rName ); + Reference xSheet; + if ( !(aAny >>= xSheet) ) + return false; + + // test if sheet is hidden + + Reference xProp( xSheet, UNO_QUERY ); + if (xProp.is()) + { + bool bVisible; + Any aVisAny = xProp->getPropertyValue("IsVisible"); + if ( (aVisAny >>= bVisible) && !bVisible) + return true; // hidden + } + + // use the same data area as in OCalcTable to test for empty table + + Reference xCursor = xSheet->createCursor(); + Reference xRange( xCursor, UNO_QUERY ); + if ( xRange.is() ) + { + xCursor->collapseToSize( 1, 1 ); // single (first) cell + xCursor->collapseToCurrentRegion(); // contiguous data area + + CellRangeAddress aRangeAddr = xRange->getRangeAddress(); + if ( aRangeAddr.StartColumn == aRangeAddr.EndColumn && + aRangeAddr.StartRow == aRangeAddr.EndRow ) + { + // single cell -> check content + Reference xCell = xCursor->getCellByPosition( 0, 0 ); + if ( xCell.is() && xCell->getType() == CellContentType_EMPTY ) + return true; + } + } + + return false; +} + +static bool lcl_IsUnnamed( const Reference& xRanges, const OUString& rName ) +{ + bool bUnnamed = false; + + Any aAny = xRanges->getByName( rName ); + Reference xRange; + if ( aAny >>= xRange ) + { + Reference xRangeProp( xRange, UNO_QUERY ); + if ( xRangeProp.is() ) + { + try + { + Any aUserAny = xRangeProp->getPropertyValue("IsUserDefined"); + bool bUserDefined; + if ( aUserAny >>= bUserDefined ) + bUnnamed = !bUserDefined; + } + catch ( UnknownPropertyException& ) + { + // optional property + } + } + } + + return bUnnamed; +} + +Reference< XResultSet > SAL_CALL OCalcDatabaseMetaData::getTables( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, + const OUString& tableNamePattern, const Sequence< OUString >& types ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + rtl::Reference pResult = new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTables); + + // check if ORowSetValue type is given + // when no types are given then we have to return all tables e.g. TABLE + + OUString aTable("TABLE"); + + bool bTableFound = true; + sal_Int32 nLength = types.getLength(); + if(nLength) + { + bTableFound = false; + + const OUString* pIter = types.getConstArray(); + const OUString* pEnd = pIter + nLength; + for(;pIter != pEnd;++pIter) + { + if(*pIter == aTable) + { + bTableFound = true; + break; + } + } + } + if(!bTableFound) + return pResult; + + // get the sheet names from the document + + OCalcConnection::ODocHolder aDocHolder(static_cast(m_pConnection)); + const Reference& xDoc = aDocHolder.getDoc(); + if ( !xDoc.is() ) + throw SQLException(); + Reference xSheets = xDoc->getSheets(); + if ( !xSheets.is() ) + throw SQLException(); + Sequence< OUString > aSheetNames = xSheets->getElementNames(); + + ODatabaseMetaDataResultSet::ORows aRows; + sal_Int32 nSheetCount = aSheetNames.getLength(); + for (sal_Int32 nSheet=0; nSheet xDocProp( xDoc, UNO_QUERY ); + if ( xDocProp.is() ) + { + Any aRangesAny = xDocProp->getPropertyValue("DatabaseRanges"); + Reference xRanges; + if ( aRangesAny >>= xRanges ) + { + Sequence< OUString > aDBNames = xRanges->getElementNames(); + sal_Int32 nDBCount = aDBNames.getLength(); + for (sal_Int32 nRange=0; nRangesetRows(std::move(aRows)); + + return pResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CDriver.cxx b/connectivity/source/drivers/calc/CDriver.cxx new file mode 100644 index 000000000..b7b11cc29 --- /dev/null +++ b/connectivity/source/drivers/calc/CDriver.cxx @@ -0,0 +1,95 @@ +/* -*- 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 + +using namespace connectivity::calc; +using namespace connectivity::file; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; + + +// ServiceInfo + +OUString SAL_CALL ODriver::getImplementationName( ) +{ + return "com.sun.star.comp.sdbc.calc.ODriver"; +} + +// service names from file::OFileDriver + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +connectivity_calc_ODriver( + css::uno::XComponentContext* context, css::uno::Sequence const&) +{ + rtl::Reference ret; + try { + ret = new ODriver(context); + } catch (...) { + } + if (ret) + ret->acquire(); + return static_cast(ret.get()); +} + + + +Reference< XConnection > SAL_CALL ODriver::connect( const OUString& url, + const Sequence< PropertyValue >& info ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if (ODriver_BASE::rBHelper.bDisposed) + throw DisposedException(); + + if ( ! acceptsURL(url) ) + return nullptr; + + rtl::Reference pCon = new OCalcConnection(this); + pCon->construct(url,info); + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + + return pCon; +} + +sal_Bool SAL_CALL ODriver::acceptsURL( const OUString& url ) +{ + return url.startsWith("sdbc:calc:"); +} + +Sequence< DriverPropertyInfo > SAL_CALL ODriver::getPropertyInfo( const OUString& url, const Sequence< PropertyValue >& /*info*/ ) +{ + if ( !acceptsURL(url) ) + { + SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + return Sequence< DriverPropertyInfo >(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CTable.cxx b/connectivity/source/drivers/calc/CTable.cxx new file mode 100644 index 000000000..53a8c8eeb --- /dev/null +++ b/connectivity/source/drivers/calc/CTable.cxx @@ -0,0 +1,652 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace connectivity; +using namespace connectivity::calc; +using namespace connectivity::file; +using namespace ::cppu; +using namespace ::dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sheet; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::util; + + +static void lcl_UpdateArea( const Reference& xUsedRange, sal_Int32& rEndCol, sal_Int32& rEndRow ) +{ + // update rEndCol, rEndRow if any non-empty cell in xUsedRange is right/below + + const Reference xUsedQuery( xUsedRange, UNO_QUERY ); + if ( !xUsedQuery.is() ) + return; + + const sal_Int16 nContentFlags = + CellFlags::STRING | CellFlags::VALUE | CellFlags::DATETIME | CellFlags::FORMULA | CellFlags::ANNOTATION; + + const Reference xUsedRanges = xUsedQuery->queryContentCells( nContentFlags ); + const Sequence aAddresses = xUsedRanges->getRangeAddresses(); + + const sal_Int32 nCount = aAddresses.getLength(); + const CellRangeAddress* pData = aAddresses.getConstArray(); + for ( sal_Int32 i=0; i& xSheet, sal_Int32& rColumnCount, sal_Int32& rRowCount ) +{ + Reference xCursor = xSheet->createCursor(); + Reference xRange( xCursor, UNO_QUERY ); + if ( !xRange.is() ) + { + rColumnCount = rRowCount = 0; + return; + } + + // first find the contiguous cell area starting at A1 + + xCursor->collapseToSize( 1, 1 ); // single (first) cell + xCursor->collapseToCurrentRegion(); // contiguous data area + + CellRangeAddress aRegionAddr = xRange->getRangeAddress(); + sal_Int32 nEndCol = aRegionAddr.EndColumn; + sal_Int32 nEndRow = aRegionAddr.EndRow; + + Reference xUsed( xCursor, UNO_QUERY ); + if ( xUsed.is() ) + { + // The used area from XUsedAreaCursor includes visible attributes. + // If the used area is larger than the contiguous cell area, find non-empty + // cells in that area. + + xUsed->gotoEndOfUsedArea( false ); + CellRangeAddress aUsedAddr = xRange->getRangeAddress(); + + if ( aUsedAddr.EndColumn > aRegionAddr.EndColumn ) + { + Reference xUsedRange = xSheet->getCellRangeByPosition( + aRegionAddr.EndColumn + 1, 0, aUsedAddr.EndColumn, aUsedAddr.EndRow ); + lcl_UpdateArea( xUsedRange, nEndCol, nEndRow ); + } + + if ( aUsedAddr.EndRow > aRegionAddr.EndRow ) + { + // only up to the last column of aRegionAddr, the other columns are handled above + Reference xUsedRange = xSheet->getCellRangeByPosition( + 0, aRegionAddr.EndRow + 1, aRegionAddr.EndColumn, aUsedAddr.EndRow ); + lcl_UpdateArea( xUsedRange, nEndCol, nEndRow ); + } + } + + rColumnCount = nEndCol + 1; // number of columns + rRowCount = nEndRow; // first row (headers) is not counted +} + +static CellContentType lcl_GetContentOrResultType( const Reference& xCell ) +{ + CellContentType eCellType = xCell->getType(); + if ( eCellType == CellContentType_FORMULA ) + { + Reference xProp( xCell, UNO_QUERY ); + try + { + xProp->getPropertyValue( "CellContentType" ) >>= eCellType; // type of cell content + } + catch (UnknownPropertyException&) + { + eCellType = CellContentType_VALUE; // if CellContentType property not available + } + } + return eCellType; +} + +static Reference lcl_GetUsedCell( const Reference& xSheet, sal_Int32 nDocColumn, sal_Int32 nDocRow ) +{ + Reference xCell = xSheet->getCellByPosition( nDocColumn, nDocRow ); + if ( xCell.is() && xCell->getType() == CellContentType_EMPTY ) + { + // get first non-empty cell + + Reference xAddr( xSheet, UNO_QUERY ); + if (xAddr.is()) + { + CellRangeAddress aTotalRange = xAddr->getRangeAddress(); + sal_Int32 nLastRow = aTotalRange.EndRow; + Reference xQuery( xSheet->getCellRangeByPosition( nDocColumn, nDocRow, nDocColumn, nLastRow ), UNO_QUERY ); + if (xQuery.is()) + { + // queryIntersection to get a ranges object + Reference xRanges = xQuery->queryIntersection( aTotalRange ); + if (xRanges.is()) + { + Reference xCells = xRanges->getCells(); + if (xCells.is()) + { + Reference xEnum = xCells->createEnumeration(); + if ( xEnum.is() && xEnum->hasMoreElements() ) + { + // get first non-empty cell from enumeration + xCell.set(xEnum->nextElement(),UNO_QUERY); + } + // otherwise, keep empty cell + } + } + } + } + } + return xCell; +} + +static bool lcl_HasTextInColumn( const Reference& xSheet, sal_Int32 nDocColumn, sal_Int32 nDocRow ) +{ + // look for any text cell or text result in the column + + Reference xAddr( xSheet, UNO_QUERY ); + if (!xAddr) + return false; + CellRangeAddress aTotalRange = xAddr->getRangeAddress(); + sal_Int32 nLastRow = aTotalRange.EndRow; + Reference xQuery( xSheet->getCellRangeByPosition( nDocColumn, nDocRow, nDocColumn, nLastRow ), UNO_QUERY ); + if (!xQuery) + return false; + + // are there text cells in the column? + Reference xTextContent = xQuery->queryContentCells( CellFlags::STRING ); + if ( xTextContent.is() && xTextContent->hasElements() ) + return true; + + // are there formulas with text results in the column? + Reference xTextFormula = xQuery->queryFormulaCells( FormulaResult::STRING ); + if ( xTextFormula.is() && xTextFormula->hasElements() ) + return true; + + return false; +} + +static void lcl_GetColumnInfo( const Reference& xSheet, const Reference& xFormats, + sal_Int32 nDocColumn, sal_Int32 nStartRow, bool bHasHeaders, + OUString& rName, sal_Int32& rDataType, bool& rCurrency ) +{ + //! avoid duplicate field names + + // get column name from first row, if range contains headers + + if ( bHasHeaders ) + { + Reference xHeaderText( xSheet->getCellByPosition( nDocColumn, nStartRow ), UNO_QUERY ); + if ( xHeaderText.is() ) + rName = xHeaderText->getString(); + } + + // get column type from first data row + + sal_Int32 nDataRow = nStartRow; + if ( bHasHeaders ) + ++nDataRow; + Reference xDataCell = lcl_GetUsedCell( xSheet, nDocColumn, nDataRow ); + + Reference xProp( xDataCell, UNO_QUERY ); + if ( !xProp.is() ) + return; + + rCurrency = false; // set to true for currency below + + const CellContentType eCellType = lcl_GetContentOrResultType( xDataCell ); + // #i35178# use "text" type if there is any text cell in the column + if ( eCellType == CellContentType_TEXT || lcl_HasTextInColumn( xSheet, nDocColumn, nDataRow ) ) + rDataType = DataType::VARCHAR; + else if ( eCellType == CellContentType_VALUE ) + { + // get number format to distinguish between different types + + sal_Int16 nNumType = NumberFormat::NUMBER; + try + { + sal_Int32 nKey = 0; + + if ( xProp->getPropertyValue( "NumberFormat" ) >>= nKey ) + { + const Reference xFormat = xFormats->getByKey( nKey ); + if ( xFormat.is() ) + { + xFormat->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE) ) >>= nNumType; + } + } + } + catch ( Exception& ) + { + } + + if ( nNumType & NumberFormat::TEXT ) + rDataType = DataType::VARCHAR; + else if ( nNumType & NumberFormat::NUMBER ) + rDataType = DataType::DECIMAL; + else if ( nNumType & NumberFormat::CURRENCY ) + { + rCurrency = true; + rDataType = DataType::DECIMAL; + } + else if ( ( nNumType & NumberFormat::DATETIME ) == NumberFormat::DATETIME ) + { + // NumberFormat::DATETIME is DATE | TIME + rDataType = DataType::TIMESTAMP; + } + else if ( nNumType & NumberFormat::DATE ) + rDataType = DataType::DATE; + else if ( nNumType & NumberFormat::TIME ) + rDataType = DataType::TIME; + else if ( nNumType & NumberFormat::LOGICAL ) + rDataType = DataType::BIT; + else + rDataType = DataType::DECIMAL; + } + else + { + // whole column empty + rDataType = DataType::VARCHAR; + } +} + + +static void lcl_SetValue( ORowSetValue& rValue, const Reference& xSheet, + sal_Int32 nStartCol, sal_Int32 nStartRow, bool bHasHeaders, + const ::Date& rNullDate, + sal_Int32 nDBRow, sal_Int32 nDBColumn, sal_Int32 nType ) +{ + sal_Int32 nDocColumn = nStartCol + nDBColumn - 1; // database counts from 1 + sal_Int32 nDocRow = nStartRow + nDBRow - 1; + if (bHasHeaders) + ++nDocRow; + + const Reference xCell = xSheet->getCellByPosition( nDocColumn, nDocRow ); + if ( !xCell.is() ) + return; + + CellContentType eCellType = lcl_GetContentOrResultType( xCell ); + switch (nType) + { + case DataType::VARCHAR: + if ( eCellType == CellContentType_EMPTY ) + rValue.setNull(); + else + { + // #i25840# still let Calc convert numbers to text + const Reference xText( xCell, UNO_QUERY ); + if ( xText.is() ) + rValue = xText->getString(); + } + break; + case DataType::DECIMAL: + if ( eCellType == CellContentType_VALUE ) + rValue = xCell->getValue(); // double + else + rValue.setNull(); + break; + case DataType::BIT: + if ( eCellType == CellContentType_VALUE ) + rValue = xCell->getValue() != 0.0; + else + rValue.setNull(); + break; + case DataType::DATE: + if ( eCellType == CellContentType_VALUE ) + { + ::Date aDate( rNullDate ); + aDate.AddDays(::rtl::math::approxFloor( xCell->getValue() )); + rValue = aDate.GetUNODate(); + } + else + rValue.setNull(); + break; + case DataType::TIME: + if ( eCellType == CellContentType_VALUE ) + { + double fCellVal = xCell->getValue(); + double fTime = fCellVal - rtl::math::approxFloor( fCellVal ); + sal_Int64 nIntTime = static_cast(rtl::math::round( fTime * static_cast(::tools::Time::nanoSecPerDay) )); + if ( nIntTime == ::tools::Time::nanoSecPerDay) + nIntTime = 0; // 23:59:59.9999999995 and above is 00:00:00.00 + css::util::Time aTime; + aTime.NanoSeconds = static_cast( nIntTime % ::tools::Time::nanoSecPerSec ); + nIntTime /= ::tools::Time::nanoSecPerSec; + aTime.Seconds = static_cast( nIntTime % 60 ); + nIntTime /= 60; + aTime.Minutes = static_cast( nIntTime % 60 ); + nIntTime /= 60; + OSL_ENSURE( nIntTime < 24, "error in time calculation" ); + aTime.Hours = static_cast(nIntTime); + rValue = aTime; + } + else + rValue.setNull(); + break; + case DataType::TIMESTAMP: + if ( eCellType == CellContentType_VALUE ) + { + double fCellVal = xCell->getValue(); + double fDays = ::rtl::math::approxFloor( fCellVal ); + double fTime = fCellVal - fDays; + tools::Long nIntDays = static_cast(fDays); + sal_Int64 nIntTime = ::rtl::math::round( fTime * static_cast(::tools::Time::nanoSecPerDay) ); + if ( nIntTime == ::tools::Time::nanoSecPerDay ) + { + nIntTime = 0; // 23:59:59.9999999995 and above is 00:00:00.00 + ++nIntDays; // (next day) + } + + css::util::DateTime aDateTime; + + aDateTime.NanoSeconds = static_cast( nIntTime % ::tools::Time::nanoSecPerSec ); + nIntTime /= ::tools::Time::nanoSecPerSec; + aDateTime.Seconds = static_cast( nIntTime % 60 ); + nIntTime /= 60; + aDateTime.Minutes = static_cast( nIntTime % 60 ); + nIntTime /= 60; + OSL_ENSURE( nIntTime < 24, "error in time calculation" ); + aDateTime.Hours = static_cast(nIntTime); + + ::Date aDate( rNullDate ); + aDate.AddDays( nIntDays ); + aDateTime.Day = aDate.GetDay(); + aDateTime.Month = aDate.GetMonth(); + aDateTime.Year = aDate.GetYear(); + + rValue = aDateTime; + } + else + rValue.setNull(); + break; + } // switch (nType) + +// rValue.setTypeKind(nType); +} + + +static OUString lcl_GetColumnStr( sal_Int32 nColumn ) +{ + if ( nColumn < 26 ) + return OUString( static_cast( 'A' + nColumn ) ); + else + { + OUStringBuffer aBuffer(2); + aBuffer.setLength( 2 ); + aBuffer[0] = static_cast( 'A' + ( nColumn / 26 ) - 1 ); + aBuffer[1] = static_cast( 'A' + ( nColumn % 26 ) ); + return aBuffer.makeStringAndClear(); + } +} + +void OCalcTable::fillColumns() +{ + if ( !m_xSheet.is() ) + throw SQLException(); + + OUString aTypeName; + ::comphelper::UStringMixEqual aCase(m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()); + const bool bStoresMixedCaseQuotedIdentifiers = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers(); + + for (sal_Int32 i = 0; i < m_nDataCols; i++) + { + OUString aColumnName; + sal_Int32 eType = DataType::OTHER; + bool bCurrency = false; + + lcl_GetColumnInfo( m_xSheet, m_xFormats, m_nStartCol + i, m_nStartRow, m_bHasHeaders, + aColumnName, eType, bCurrency ); + + if ( aColumnName.isEmpty() ) + aColumnName = lcl_GetColumnStr( i ); + + sal_Int32 nPrecision = 0; //! ... + sal_Int32 nDecimals = 0; //! ... + + switch ( eType ) + { + case DataType::VARCHAR: + aTypeName = "VARCHAR"; + break; + case DataType::DECIMAL: + aTypeName = "DECIMAL"; + break; + case DataType::BIT: + aTypeName = "BOOL"; + break; + case DataType::DATE: + aTypeName = "DATE"; + break; + case DataType::TIME: + aTypeName = "TIME"; + break; + case DataType::TIMESTAMP: + aTypeName = "TIMESTAMP"; + break; + default: + SAL_WARN( "connectivity.drivers","missing type name"); + aTypeName.clear(); + } + + // check if the column name already exists + OUString aAlias = aColumnName; + OSQLColumns::const_iterator aFind = connectivity::find(m_aColumns->begin(),m_aColumns->end(),aAlias,aCase); + sal_Int32 nExprCnt = 0; + while(aFind != m_aColumns->end()) + { + aAlias = aColumnName + OUString::number(++nExprCnt); + aFind = connectivity::find(m_aColumns->begin(),m_aColumns->end(),aAlias,aCase); + } + + rtl::Reference pColumn = new sdbcx::OColumn( aAlias, aTypeName, OUString(),OUString(), + ColumnValue::NULLABLE, nPrecision, nDecimals, + eType, false, false, bCurrency, + bStoresMixedCaseQuotedIdentifiers, + m_CatalogName, getSchema(), getName()); + m_aColumns->push_back(pColumn); + m_aTypes.push_back(eType); + } +} + + +OCalcTable::OCalcTable(sdbcx::OCollection* _pTables,OCalcConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName + ) : OCalcTable_BASE(_pTables,_pConnection,Name, + Type, + Description, + SchemaName, + CatalogName) + ,m_pCalcConnection(_pConnection) + ,m_nStartCol(0) + ,m_nStartRow(0) + ,m_nDataCols(0) + ,m_bHasHeaders(false) + ,m_aNullDate(::Date::EMPTY) +{ +} + +void OCalcTable::construct() +{ + // get sheet object + Reference< XSpreadsheetDocument> xDoc = m_pCalcConnection->acquireDoc(); + if (xDoc.is()) + { + Reference xSheets = xDoc->getSheets(); + if ( xSheets.is() && xSheets->hasByName( m_Name ) ) + { + m_xSheet.set(xSheets->getByName( m_Name ),UNO_QUERY); + if ( m_xSheet.is() ) + { + lcl_GetDataArea( m_xSheet, m_nDataCols, m_nDataRows ); + m_bHasHeaders = true; + // whole sheet is always assumed to include a header row + } + } + else // no sheet -> try database range + { + Reference xDocProp( xDoc, UNO_QUERY ); + if ( xDocProp.is() ) + { + Reference xRanges(xDocProp->getPropertyValue("DatabaseRanges"),UNO_QUERY); + + if ( xRanges.is() && xRanges->hasByName( m_Name ) ) + { + Reference xDBRange(xRanges->getByName( m_Name ),UNO_QUERY); + Reference xRefer( xDBRange, UNO_QUERY ); + if ( xRefer.is() ) + { + // Header flag is always stored with database range + // Get flag from FilterDescriptor + + bool bRangeHeader = true; + Reference xFiltProp( xDBRange->getFilterDescriptor(), UNO_QUERY ); + if ( xFiltProp.is() ) + xFiltProp->getPropertyValue("ContainsHeader") >>= bRangeHeader; + + Reference xSheetRange( xRefer->getReferredCells(), UNO_QUERY ); + Reference xAddr( xSheetRange, UNO_QUERY ); + if ( xSheetRange.is() && xAddr.is() ) + { + m_xSheet = xSheetRange->getSpreadsheet(); + CellRangeAddress aRangeAddr = xAddr->getRangeAddress(); + m_nStartCol = aRangeAddr.StartColumn; + m_nStartRow = aRangeAddr.StartRow; + m_nDataCols = aRangeAddr.EndColumn - m_nStartCol + 1; + // m_nDataRows is excluding header row + m_nDataRows = aRangeAddr.EndRow - m_nStartRow; + if ( !bRangeHeader ) + { + // m_nDataRows counts the whole range + m_nDataRows += 1; + } + + m_bHasHeaders = bRangeHeader; + } + } + } + } + } + + Reference xSupp( xDoc, UNO_QUERY ); + if (xSupp.is()) + m_xFormats = xSupp->getNumberFormats(); + + Reference xProp( xDoc, UNO_QUERY ); + if (xProp.is()) + { + css::util::Date aDateStruct; + if ( xProp->getPropertyValue("NullDate") >>= aDateStruct ) + m_aNullDate = ::Date( aDateStruct.Day, aDateStruct.Month, aDateStruct.Year ); + } + } + + //! default if no null date available? + + fillColumns(); + + refreshColumns(); +} + +void SAL_CALL OCalcTable::disposing() +{ + OFileTable::disposing(); + ::osl::MutexGuard aGuard(m_aMutex); + m_aColumns = nullptr; + if ( m_pCalcConnection ) + m_pCalcConnection->releaseDoc(); + m_pCalcConnection = nullptr; + +} + +const Sequence< sal_Int8 > & OCalcTable::getUnoTunnelId() +{ + static const comphelper::UnoIdInit implId; + return implId.getSeq(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OCalcTable::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf{}); +} + +bool OCalcTable::fetchRow( OValueRefRow& _rRow, const OSQLColumns & _rCols, + bool bRetrieveData ) +{ + // read the bookmark + + _rRow->setDeleted(false); + *(*_rRow)[0] = m_nFilePos; + + if (!bRetrieveData) + return true; + + // fields + + const OValueRefVector::size_type nCount = std::min(_rRow->size(), _rCols.size() + 1); + for (OValueRefVector::size_type i = 1; i < nCount; i++) + { + if ( (*_rRow)[i]->isBound() ) + { + sal_Int32 nType = m_aTypes[i-1]; + + lcl_SetValue( (*_rRow)[i]->get(), m_xSheet, m_nStartCol, m_nStartRow, m_bHasHeaders, + m_aNullDate, m_nFilePos, i, nType ); + } + } + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CTables.cxx b/connectivity/source/drivers/calc/CTables.cxx new file mode 100644 index 000000000..08e5d63e3 --- /dev/null +++ b/connectivity/source/drivers/calc/CTables.cxx @@ -0,0 +1,48 @@ +/* -*- 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 + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::calc; +using namespace connectivity::file; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; + +sdbcx::ObjectType OCalcTables::createObject(const OUString& _rName) +{ + rtl::Reference pTable = new OCalcTable(this, static_cast(static_cast(m_rParent).getConnection()), + _rName,"TABLE"); + pTable->construct(); + return pTable; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/calc.component b/connectivity/source/drivers/calc/calc.component new file mode 100644 index 000000000..a4fc7d8f1 --- /dev/null +++ b/connectivity/source/drivers/calc/calc.component @@ -0,0 +1,27 @@ + + + + + + + + + -- cgit v1.2.3