|  | /************************************************************** | 
|  | * | 
|  | * 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 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, | 
|  | * software distributed under the License is distributed on an | 
|  | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
|  | * KIND, either express or implied.  See the License for the | 
|  | * specific language governing permissions and limitations | 
|  | * under the License. | 
|  | * | 
|  | *************************************************************/ | 
|  |  | 
|  |  | 
|  |  | 
|  | // MARKER(update_precomp.py): autogen include statement, do not remove | 
|  | #include "precompiled_connectivity.hxx" | 
|  | #include "calc/CTable.hxx" | 
|  | #include <com/sun/star/sdbc/ColumnValue.hpp> | 
|  | #include <com/sun/star/sdbc/DataType.hpp> | 
|  | //#ifndef _COM_SUN_STAR_UCB_XCONTENTACCESS_HPP_ | 
|  | //#include <com/sun/star/ucb/XContentAccess.hpp> | 
|  | //#endif | 
|  | #include <com/sun/star/sdbc/XRow.hpp> | 
|  | #include <com/sun/star/sheet/XSpreadsheetDocument.hpp> | 
|  | #include <com/sun/star/sheet/XSpreadsheet.hpp> | 
|  | #include <com/sun/star/sheet/XCellRangeAddressable.hpp> | 
|  | #include <com/sun/star/sheet/XCellRangesQuery.hpp> | 
|  | #include <com/sun/star/sheet/XDatabaseRanges.hpp> | 
|  | #include <com/sun/star/sheet/XDatabaseRange.hpp> | 
|  | #include <com/sun/star/sheet/XCellRangeReferrer.hpp> | 
|  | #include <com/sun/star/sheet/XUsedAreaCursor.hpp> | 
|  | #include <com/sun/star/sheet/CellFlags.hpp> | 
|  | #include <com/sun/star/sheet/FormulaResult.hpp> | 
|  | #include <com/sun/star/util/NumberFormat.hpp> | 
|  | #include <com/sun/star/util/XNumberFormatsSupplier.hpp> | 
|  | #include <com/sun/star/text/XText.hpp> | 
|  | #include <svl/converter.hxx> | 
|  | #include "calc/CConnection.hxx" | 
|  | #include "calc/CColumns.hxx" | 
|  | #include "connectivity/sdbcx/VColumn.hxx" | 
|  | #include <rtl/ustrbuf.hxx> | 
|  | #include <osl/thread.h> | 
|  | #include <tools/config.hxx> | 
|  | #include <comphelper/sequence.hxx> | 
|  | #include <svl/zforlist.hxx> | 
|  | #include <rtl/math.hxx> | 
|  | #include <comphelper/extract.hxx> | 
|  | #include <connectivity/dbexception.hxx> | 
|  | #include <connectivity/dbconversion.hxx> | 
|  | #include <comphelper/types.hxx> | 
|  | #include <rtl/logfile.hxx> | 
|  |  | 
|  | 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; | 
|  |  | 
|  | // ------------------------------------------------------------------------- | 
|  |  | 
|  | void lcl_UpdateArea( const Reference<XCellRange>& xUsedRange, sal_Int32& rEndCol, sal_Int32& rEndRow ) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_UpdateArea" ); | 
|  | //	update rEndCol, rEndRow if any non-empty cell in xUsedRange is right/below | 
|  |  | 
|  | const Reference<XCellRangesQuery> xUsedQuery( xUsedRange, UNO_QUERY ); | 
|  | if ( xUsedQuery.is() ) | 
|  | { | 
|  | const sal_Int16 nContentFlags = | 
|  | CellFlags::STRING | CellFlags::VALUE | CellFlags::DATETIME | CellFlags::FORMULA | CellFlags::ANNOTATION; | 
|  |  | 
|  | const Reference<XSheetCellRanges> xUsedRanges = xUsedQuery->queryContentCells( nContentFlags ); | 
|  | const Sequence<CellRangeAddress> aAddresses = xUsedRanges->getRangeAddresses(); | 
|  |  | 
|  | const sal_Int32 nCount = aAddresses.getLength(); | 
|  | const CellRangeAddress* pData = aAddresses.getConstArray(); | 
|  | for ( sal_Int32 i=0; i<nCount; i++ ) | 
|  | { | 
|  | rEndCol = pData[i].EndColumn > rEndCol ? pData[i].EndColumn : rEndCol; | 
|  | rEndRow = pData[i].EndRow    > rEndRow ? pData[i].EndRow    : rEndRow; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void lcl_GetDataArea( const Reference<XSpreadsheet>& xSheet, sal_Int32& rColumnCount, sal_Int32& rRowCount ) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetDataArea" ); | 
|  | Reference<XSheetCellCursor> xCursor = xSheet->createCursor(); | 
|  | Reference<XCellRangeAddressable> 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<XUsedAreaCursor> 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( sal_False ); | 
|  | CellRangeAddress aUsedAddr = xRange->getRangeAddress(); | 
|  |  | 
|  | if ( aUsedAddr.EndColumn > aRegionAddr.EndColumn ) | 
|  | { | 
|  | Reference<XCellRange> 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<XCellRange> 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 | 
|  | } | 
|  |  | 
|  | CellContentType lcl_GetContentOrResultType( const Reference<XCell>& xCell ) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetContentOrResultType" ); | 
|  | CellContentType eCellType = xCell->getType(); | 
|  | if ( eCellType == CellContentType_FORMULA ) | 
|  | { | 
|  | static const ::rtl::OUString s_sFormulaResultType(RTL_CONSTASCII_USTRINGPARAM("FormulaResultType")); | 
|  | Reference<XPropertySet> xProp( xCell, UNO_QUERY ); | 
|  | try | 
|  | { | 
|  | xProp->getPropertyValue( s_sFormulaResultType ) >>= eCellType;		// type of formula result | 
|  | } | 
|  | catch (UnknownPropertyException&) | 
|  | { | 
|  | eCellType = CellContentType_VALUE;	// if FormulaResultType property not available | 
|  | } | 
|  | } | 
|  | return eCellType; | 
|  | } | 
|  |  | 
|  | Reference<XCell> lcl_GetUsedCell( const Reference<XSpreadsheet>& xSheet, sal_Int32 nDocColumn, sal_Int32 nDocRow ) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetUsedCell" ); | 
|  | Reference<XCell> xCell = xSheet->getCellByPosition( nDocColumn, nDocRow ); | 
|  | if ( xCell.is() && xCell->getType() == CellContentType_EMPTY ) | 
|  | { | 
|  | //	get first non-empty cell | 
|  |  | 
|  | Reference<XCellRangeAddressable> xAddr( xSheet, UNO_QUERY ); | 
|  | if (xAddr.is()) | 
|  | { | 
|  | CellRangeAddress aTotalRange = xAddr->getRangeAddress(); | 
|  | sal_Int32 nLastRow = aTotalRange.EndRow; | 
|  | Reference<XCellRangesQuery> xQuery( xSheet->getCellRangeByPosition( nDocColumn, nDocRow, nDocColumn, nLastRow ), UNO_QUERY ); | 
|  | if (xQuery.is()) | 
|  | { | 
|  | // queryIntersection to get a ranges object | 
|  | Reference<XSheetCellRanges> xRanges = xQuery->queryIntersection( aTotalRange ); | 
|  | if (xRanges.is()) | 
|  | { | 
|  | Reference<XEnumerationAccess> xCells = xRanges->getCells(); | 
|  | if (xCells.is()) | 
|  | { | 
|  | Reference<XEnumeration> 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; | 
|  | } | 
|  |  | 
|  | bool lcl_HasTextInColumn( const Reference<XSpreadsheet>& xSheet, sal_Int32 nDocColumn, sal_Int32 nDocRow ) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_HasTextInColumn" ); | 
|  | // look for any text cell or text result in the column | 
|  |  | 
|  | Reference<XCellRangeAddressable> xAddr( xSheet, UNO_QUERY ); | 
|  | if (xAddr.is()) | 
|  | { | 
|  | CellRangeAddress aTotalRange = xAddr->getRangeAddress(); | 
|  | sal_Int32 nLastRow = aTotalRange.EndRow; | 
|  | Reference<XCellRangesQuery> xQuery( xSheet->getCellRangeByPosition( nDocColumn, nDocRow, nDocColumn, nLastRow ), UNO_QUERY ); | 
|  | if (xQuery.is()) | 
|  | { | 
|  | // are there text cells in the column? | 
|  | Reference<XSheetCellRanges> xTextContent = xQuery->queryContentCells( CellFlags::STRING ); | 
|  | if ( xTextContent.is() && xTextContent->hasElements() ) | 
|  | return true; | 
|  |  | 
|  | // are there formulas with text results in the column? | 
|  | Reference<XSheetCellRanges> xTextFormula = xQuery->queryFormulaCells( FormulaResult::STRING ); | 
|  | if ( xTextFormula.is() && xTextFormula->hasElements() ) | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void lcl_GetColumnInfo( const Reference<XSpreadsheet>& xSheet, const Reference<XNumberFormats>& xFormats, | 
|  | sal_Int32 nDocColumn, sal_Int32 nStartRow, sal_Bool bHasHeaders, | 
|  | ::rtl::OUString& rName, sal_Int32& rDataType, sal_Bool& rCurrency ) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetColumnInfo" ); | 
|  | //!	avoid duplicate field names | 
|  |  | 
|  | //	get column name from first row, if range contains headers | 
|  |  | 
|  | if ( bHasHeaders ) | 
|  | { | 
|  | Reference<XText> 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<XCell> xDataCell = lcl_GetUsedCell( xSheet, nDocColumn, nDataRow ); | 
|  |  | 
|  | Reference<XPropertySet> xProp( xDataCell, UNO_QUERY ); | 
|  | if ( xProp.is() ) | 
|  | { | 
|  | rCurrency = sal_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 | 
|  | { | 
|  | static ::rtl::OUString s_NumberFormat(RTL_CONSTASCII_USTRINGPARAM("NumberFormat")); | 
|  | sal_Int32 nKey = 0; | 
|  |  | 
|  | if ( xProp->getPropertyValue( s_NumberFormat ) >>= nKey ) | 
|  | { | 
|  | const Reference<XPropertySet> 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 = sal_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; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // ------------------------------------------------------------------------- | 
|  |  | 
|  | void lcl_SetValue( ORowSetValue& rValue, const Reference<XSpreadsheet>& xSheet, | 
|  | sal_Int32 nStartCol, sal_Int32 nStartRow, sal_Bool bHasHeaders, | 
|  | const ::Date& rNullDate, | 
|  | sal_Int32 nDBRow, sal_Int32 nDBColumn, sal_Int32 nType ) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_SetValue" ); | 
|  | sal_Int32 nDocColumn = nStartCol + nDBColumn - 1;	// database counts from 1 | 
|  | sal_Int32 nDocRow = nStartRow + nDBRow - 1; | 
|  | if (bHasHeaders) | 
|  | ++nDocRow; | 
|  |  | 
|  | const Reference<XCell> xCell = xSheet->getCellByPosition( nDocColumn, nDocRow ); | 
|  | if ( xCell.is() ) | 
|  | { | 
|  | 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> 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 = (sal_Bool)( xCell->getValue() != 0.0 ); | 
|  | else | 
|  | rValue.setNull(); | 
|  | break; | 
|  | case DataType::DATE: | 
|  | if ( eCellType == CellContentType_VALUE ) | 
|  | { | 
|  | ::Date aDate( rNullDate ); | 
|  | aDate += (long)::rtl::math::approxFloor( xCell->getValue() ); | 
|  | ::com::sun::star::util::Date aDateStruct( aDate.GetDay(), aDate.GetMonth(), aDate.GetYear() ); | 
|  | rValue = aDateStruct; | 
|  | } | 
|  | else | 
|  | rValue.setNull(); | 
|  | break; | 
|  | case DataType::TIME: | 
|  | if ( eCellType == CellContentType_VALUE ) | 
|  | { | 
|  | double fCellVal = xCell->getValue(); | 
|  | double fTime = fCellVal - rtl::math::approxFloor( fCellVal ); | 
|  | long nIntTime = (long)rtl::math::round( fTime * 8640000.0 ); | 
|  | if ( nIntTime == 8640000 ) | 
|  | nIntTime = 0;						// 23:59:59.995 and above is 00:00:00.00 | 
|  | ::com::sun::star::util::Time aTime; | 
|  | aTime.HundredthSeconds = (sal_uInt16)( nIntTime % 100 ); | 
|  | nIntTime /= 100; | 
|  | aTime.Seconds = (sal_uInt16)( nIntTime % 60 ); | 
|  | nIntTime /= 60; | 
|  | aTime.Minutes = (sal_uInt16)( nIntTime % 60 ); | 
|  | nIntTime /= 60; | 
|  | OSL_ENSURE( nIntTime < 24, "error in time calculation" ); | 
|  | aTime.Hours = (sal_uInt16) 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; | 
|  | long nIntDays = (long)fDays; | 
|  | long nIntTime = (long)::rtl::math::round( fTime * 8640000.0 ); | 
|  | if ( nIntTime == 8640000 ) | 
|  | { | 
|  | nIntTime = 0;						// 23:59:59.995 and above is 00:00:00.00 | 
|  | ++nIntDays;							// (next day) | 
|  | } | 
|  |  | 
|  | ::com::sun::star::util::DateTime aDateTime; | 
|  |  | 
|  | aDateTime.HundredthSeconds = (sal_uInt16)( nIntTime % 100 ); | 
|  | nIntTime /= 100; | 
|  | aDateTime.Seconds = (sal_uInt16)( nIntTime % 60 ); | 
|  | nIntTime /= 60; | 
|  | aDateTime.Minutes = (sal_uInt16)( nIntTime % 60 ); | 
|  | nIntTime /= 60; | 
|  | OSL_ENSURE( nIntTime < 24, "error in time calculation" ); | 
|  | aDateTime.Hours = (sal_uInt16) nIntTime; | 
|  |  | 
|  | ::Date aDate( rNullDate ); | 
|  | aDate += nIntDays; | 
|  | aDateTime.Day = aDate.GetDay(); | 
|  | aDateTime.Month = aDate.GetMonth(); | 
|  | aDateTime.Year = aDate.GetYear(); | 
|  |  | 
|  | rValue = aDateTime; | 
|  | } | 
|  | else | 
|  | rValue.setNull(); | 
|  | break; | 
|  | } // switch (nType) | 
|  | } | 
|  |  | 
|  | //	rValue.setTypeKind(nType); | 
|  | } | 
|  |  | 
|  | // ------------------------------------------------------------------------- | 
|  |  | 
|  | ::rtl::OUString lcl_GetColumnStr( sal_Int32 nColumn ) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetColumnStr" ); | 
|  | if ( nColumn < 26 ) | 
|  | return ::rtl::OUString::valueOf( (sal_Unicode) ( 'A' + nColumn ) ); | 
|  | else | 
|  | { | 
|  | ::rtl::OUStringBuffer aBuffer(2); | 
|  | aBuffer.setLength( 2 ); | 
|  | aBuffer.setCharAt( 0, (sal_Unicode) ( 'A' + ( nColumn / 26 ) - 1 ) ); | 
|  | aBuffer.setCharAt( 1, (sal_Unicode) ( 'A' + ( nColumn % 26 ) ) ); | 
|  | return aBuffer.makeStringAndClear(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void OCalcTable::fillColumns() | 
|  | { | 
|  | RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::fillColumns" ); | 
|  | if ( !m_xSheet.is() ) | 
|  | throw SQLException(); | 
|  |  | 
|  | String aStrFieldName; | 
|  | aStrFieldName.AssignAscii("Column"); | 
|  | ::rtl::OUString aTypeName; | 
|  | ::comphelper::UStringMixEqual aCase(m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()); | 
|  | const sal_Bool bStoresMixedCaseQuotedIdentifiers = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers(); | 
|  |  | 
|  | for (sal_Int32 i = 0; i < m_nDataCols; i++) | 
|  | { | 
|  | ::rtl::OUString aColumnName; | 
|  | sal_Int32 eType = DataType::OTHER; | 
|  | sal_Bool bCurrency = sal_False; | 
|  |  | 
|  | lcl_GetColumnInfo( m_xSheet, m_xFormats, m_nStartCol + i, m_nStartRow, m_bHasHeaders, | 
|  | aColumnName, eType, bCurrency ); | 
|  |  | 
|  | if ( !aColumnName.getLength() ) | 
|  | aColumnName = lcl_GetColumnStr( i ); | 
|  |  | 
|  | sal_Int32 nPrecision = 0;	//! ... | 
|  | sal_Int32 nDecimals = 0;	//! ... | 
|  |  | 
|  | switch ( eType ) | 
|  | { | 
|  | case DataType::VARCHAR: | 
|  | { | 
|  | static const ::rtl::OUString s_sType(RTL_CONSTASCII_USTRINGPARAM("VARCHAR")); | 
|  | aTypeName = s_sType; | 
|  | } | 
|  | break; | 
|  | case DataType::DECIMAL: | 
|  | aTypeName = ::rtl::OUString::createFromAscii("DECIMAL"); | 
|  | break; | 
|  | case DataType::BIT: | 
|  | aTypeName = ::rtl::OUString::createFromAscii("BOOL"); | 
|  | break; | 
|  | case DataType::DATE: | 
|  | aTypeName = ::rtl::OUString::createFromAscii("DATE"); | 
|  | break; | 
|  | case DataType::TIME: | 
|  | aTypeName = ::rtl::OUString::createFromAscii("TIME"); | 
|  | break; | 
|  | case DataType::TIMESTAMP: | 
|  | aTypeName = ::rtl::OUString::createFromAscii("TIMESTAMP"); | 
|  | break; | 
|  | default: | 
|  | OSL_ASSERT("missing type name"); | 
|  | aTypeName = ::rtl::OUString(); | 
|  | } | 
|  |  | 
|  | // check if the column name already exists | 
|  | ::rtl::OUString aAlias = aColumnName; | 
|  | OSQLColumns::Vector::const_iterator aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase); | 
|  | sal_Int32 nExprCnt = 0; | 
|  | while(aFind != m_aColumns->get().end()) | 
|  | { | 
|  | (aAlias = aColumnName) += ::rtl::OUString::valueOf((sal_Int32)++nExprCnt); | 
|  | aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase); | 
|  | } | 
|  |  | 
|  | sdbcx::OColumn* pColumn = new sdbcx::OColumn( aAlias, aTypeName, ::rtl::OUString(),::rtl::OUString(), | 
|  | ColumnValue::NULLABLE, nPrecision, nDecimals, | 
|  | eType, sal_False, sal_False, bCurrency, | 
|  | bStoresMixedCaseQuotedIdentifiers); | 
|  | Reference< XPropertySet> xCol = pColumn; | 
|  | m_aColumns->get().push_back(xCol); | 
|  | m_aTypes.push_back(eType); | 
|  | m_aPrecisions.push_back(nPrecision); | 
|  | m_aScales.push_back(nDecimals); | 
|  | } | 
|  | } | 
|  |  | 
|  | // ------------------------------------------------------------------------- | 
|  | OCalcTable::OCalcTable(sdbcx::OCollection* _pTables,OCalcConnection* _pConnection, | 
|  | const ::rtl::OUString& _Name, | 
|  | const ::rtl::OUString& _Type, | 
|  | const ::rtl::OUString& _Description , | 
|  | const ::rtl::OUString& _SchemaName, | 
|  | const ::rtl::OUString& _CatalogName | 
|  | ) : OCalcTable_BASE(_pTables,_pConnection,_Name, | 
|  | _Type, | 
|  | _Description, | 
|  | _SchemaName, | 
|  | _CatalogName) | 
|  | ,m_pConnection(_pConnection) | 
|  | ,m_nStartCol(0) | 
|  | ,m_nStartRow(0) | 
|  | ,m_nDataCols(0) | 
|  | ,m_nDataRows(0) | 
|  | ,m_bHasHeaders(sal_False) | 
|  | { | 
|  | RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::OCalcTable" ); | 
|  | } | 
|  | // ----------------------------------------------------------------------------- | 
|  | void OCalcTable::construct() | 
|  | { | 
|  | RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::construct" ); | 
|  | //	get sheet object | 
|  | Reference< XSpreadsheetDocument> xDoc = m_pConnection->acquireDoc(); | 
|  | if (xDoc.is()) | 
|  | { | 
|  | Reference<XSpreadsheets> 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 = sal_True; | 
|  | // whole sheet is always assumed to include a header row | 
|  | } | 
|  | } | 
|  | else		// no sheet -> try database range | 
|  | { | 
|  | Reference<XPropertySet> xDocProp( xDoc, UNO_QUERY ); | 
|  | if ( xDocProp.is() ) | 
|  | { | 
|  | Reference<XDatabaseRanges> xRanges(xDocProp->getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("DatabaseRanges")) ),UNO_QUERY); | 
|  |  | 
|  | if ( xRanges.is() && xRanges->hasByName( m_Name ) ) | 
|  | { | 
|  | Reference<XDatabaseRange> xDBRange(xRanges->getByName( m_Name ),UNO_QUERY); | 
|  | Reference<XCellRangeReferrer> xRefer( xDBRange, UNO_QUERY ); | 
|  | if ( xRefer.is() ) | 
|  | { | 
|  | //	Header flag is always stored with database range | 
|  | //	Get flag from FilterDescriptor | 
|  |  | 
|  | sal_Bool bRangeHeader = sal_True; | 
|  | Reference<XPropertySet> xFiltProp( xDBRange->getFilterDescriptor(), UNO_QUERY ); | 
|  | if ( xFiltProp.is() ) | 
|  | xFiltProp->getPropertyValue(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ContainsHeader"))) >>= bRangeHeader; | 
|  |  | 
|  | Reference<XSheetCellRange> xSheetRange( xRefer->getReferredCells(), UNO_QUERY ); | 
|  | Reference<XCellRangeAddressable> 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<XNumberFormatsSupplier> xSupp( xDoc, UNO_QUERY ); | 
|  | if (xSupp.is()) | 
|  | m_xFormats = xSupp->getNumberFormats(); | 
|  |  | 
|  | Reference<XPropertySet> xProp( xDoc, UNO_QUERY ); | 
|  | if (xProp.is()) | 
|  | { | 
|  | ::com::sun::star::util::Date aDateStruct; | 
|  | if ( xProp->getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("NullDate")) ) >>= aDateStruct ) | 
|  | m_aNullDate = ::Date( aDateStruct.Day, aDateStruct.Month, aDateStruct.Year ); | 
|  | } | 
|  | } | 
|  |  | 
|  | //!	default if no null date available? | 
|  |  | 
|  | fillColumns(); | 
|  |  | 
|  | refreshColumns(); | 
|  | } | 
|  | // ------------------------------------------------------------------------- | 
|  | void OCalcTable::refreshColumns() | 
|  | { | 
|  | RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::refreshColumns" ); | 
|  | ::osl::MutexGuard aGuard( m_aMutex ); | 
|  |  | 
|  | TStringVector aVector; | 
|  |  | 
|  | OSQLColumns::Vector::const_iterator aEnd = m_aColumns->get().end(); | 
|  | for(OSQLColumns::Vector::const_iterator aIter = m_aColumns->get().begin();aIter != aEnd;++aIter) | 
|  | aVector.push_back(Reference< XNamed>(*aIter,UNO_QUERY)->getName()); | 
|  |  | 
|  | if(m_pColumns) | 
|  | m_pColumns->reFill(aVector); | 
|  | else | 
|  | m_pColumns	= new OCalcColumns(this,m_aMutex,aVector); | 
|  | } | 
|  | // ------------------------------------------------------------------------- | 
|  | void OCalcTable::refreshIndexes() | 
|  | { | 
|  | RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::refreshIndexes" ); | 
|  | //	Calc table has no index | 
|  | } | 
|  |  | 
|  | // ------------------------------------------------------------------------- | 
|  | void SAL_CALL OCalcTable::disposing(void) | 
|  | { | 
|  | RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::disposing" ); | 
|  | OFileTable::disposing(); | 
|  | ::osl::MutexGuard aGuard(m_aMutex); | 
|  | m_aColumns = NULL; | 
|  | if ( m_pConnection ) | 
|  | m_pConnection->releaseDoc(); | 
|  | m_pConnection = NULL; | 
|  |  | 
|  | } | 
|  | // ------------------------------------------------------------------------- | 
|  | Sequence< Type > SAL_CALL OCalcTable::getTypes(  ) throw(RuntimeException) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::getTypes" ); | 
|  | Sequence< Type > aTypes = OTable_TYPEDEF::getTypes(); | 
|  | ::std::vector<Type> aOwnTypes; | 
|  | aOwnTypes.reserve(aTypes.getLength()); | 
|  |  | 
|  | const Type* pBegin = aTypes.getConstArray(); | 
|  | const Type* pEnd = pBegin + aTypes.getLength(); | 
|  | for(;pBegin != pEnd;++pBegin) | 
|  | { | 
|  | if(!(	*pBegin == ::getCppuType((const Reference<XKeysSupplier>*)0) || | 
|  | *pBegin == ::getCppuType((const Reference<XIndexesSupplier>*)0) || | 
|  | *pBegin == ::getCppuType((const Reference<XRename>*)0) || | 
|  | *pBegin == ::getCppuType((const Reference<XAlterTable>*)0) || | 
|  | *pBegin == ::getCppuType((const Reference<XDataDescriptorFactory>*)0))) | 
|  | aOwnTypes.push_back(*pBegin); | 
|  | } | 
|  | aOwnTypes.push_back(::getCppuType( (const Reference< ::com::sun::star::lang::XUnoTunnel > *)0 )); | 
|  |  | 
|  | const Type* pAttrs = aOwnTypes.empty() ? 0 : &aOwnTypes[0]; | 
|  | return Sequence< Type >(pAttrs, aOwnTypes.size()); | 
|  | } | 
|  |  | 
|  | // ------------------------------------------------------------------------- | 
|  | Any SAL_CALL OCalcTable::queryInterface( const Type & rType ) throw(RuntimeException) | 
|  | { | 
|  | if( rType == ::getCppuType((const Reference<XKeysSupplier>*)0) || | 
|  | rType == ::getCppuType((const Reference<XIndexesSupplier>*)0) || | 
|  | rType == ::getCppuType((const Reference<XRename>*)0) || | 
|  | rType == ::getCppuType((const Reference<XAlterTable>*)0) || | 
|  | rType == ::getCppuType((const Reference<XDataDescriptorFactory>*)0)) | 
|  | return Any(); | 
|  |  | 
|  | const Any aRet = ::cppu::queryInterface(rType,static_cast< ::com::sun::star::lang::XUnoTunnel*> (this)); | 
|  | return aRet.hasValue() ? aRet : OTable_TYPEDEF::queryInterface(rType); | 
|  | } | 
|  |  | 
|  | //-------------------------------------------------------------------------- | 
|  | Sequence< sal_Int8 > OCalcTable::getUnoTunnelImplementationId() | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::getUnoTunnelImplementationId" ); | 
|  | static ::cppu::OImplementationId * pId = 0; | 
|  | if (! pId) | 
|  | { | 
|  | ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); | 
|  | if (! pId) | 
|  | { | 
|  | static ::cppu::OImplementationId aId; | 
|  | pId = &aId; | 
|  | } | 
|  | } | 
|  | return pId->getImplementationId(); | 
|  | } | 
|  |  | 
|  | // com::sun::star::lang::XUnoTunnel | 
|  | //------------------------------------------------------------------ | 
|  | sal_Int64 OCalcTable::getSomething( const Sequence< sal_Int8 > & rId ) throw (RuntimeException) | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::getSomething" ); | 
|  | return (rId.getLength() == 16 && 0 == rtl_compareMemory(getUnoTunnelImplementationId().getConstArray(),  rId.getConstArray(), 16 ) ) | 
|  | ? reinterpret_cast< sal_Int64 >( this ) | 
|  | : OCalcTable_BASE::getSomething(rId); | 
|  | } | 
|  | //------------------------------------------------------------------ | 
|  | sal_Int32 OCalcTable::getCurrentLastPos() const | 
|  | { | 
|  | //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::getCurrentLastPos" ); | 
|  | return m_nDataRows; | 
|  | } | 
|  | //------------------------------------------------------------------ | 
|  | sal_Bool OCalcTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos) | 
|  | { | 
|  | RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::seekRow" ); | 
|  | // ---------------------------------------------------------- | 
|  | // Positionierung vorbereiten: | 
|  |  | 
|  | sal_uInt32 nNumberOfRecords = m_nDataRows; | 
|  | sal_uInt32 nTempPos = m_nFilePos; | 
|  | m_nFilePos = nCurPos; | 
|  |  | 
|  | switch(eCursorPosition) | 
|  | { | 
|  | case IResultSetHelper::NEXT: | 
|  | m_nFilePos++; | 
|  | break; | 
|  | case IResultSetHelper::PRIOR: | 
|  | if (m_nFilePos > 0) | 
|  | m_nFilePos--; | 
|  | break; | 
|  | case IResultSetHelper::FIRST: | 
|  | m_nFilePos = 1; | 
|  | break; | 
|  | case IResultSetHelper::LAST: | 
|  | m_nFilePos = nNumberOfRecords; | 
|  | break; | 
|  | case IResultSetHelper::RELATIVE: | 
|  | m_nFilePos = (((sal_Int32)m_nFilePos) + nOffset < 0) ? 0L | 
|  | : (sal_uInt32)(((sal_Int32)m_nFilePos) + nOffset); | 
|  | break; | 
|  | case IResultSetHelper::ABSOLUTE: | 
|  | case IResultSetHelper::BOOKMARK: | 
|  | m_nFilePos = (sal_uInt32)nOffset; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (m_nFilePos > (sal_Int32)nNumberOfRecords) | 
|  | m_nFilePos = (sal_Int32)nNumberOfRecords + 1; | 
|  |  | 
|  | if (m_nFilePos == 0 || m_nFilePos == (sal_Int32)nNumberOfRecords + 1) | 
|  | goto Error; | 
|  | else | 
|  | { | 
|  | //!	read buffer / setup row object etc? | 
|  | } | 
|  | goto End; | 
|  |  | 
|  | Error: | 
|  | switch(eCursorPosition) | 
|  | { | 
|  | case IResultSetHelper::PRIOR: | 
|  | case IResultSetHelper::FIRST: | 
|  | m_nFilePos = 0; | 
|  | break; | 
|  | case IResultSetHelper::LAST: | 
|  | case IResultSetHelper::NEXT: | 
|  | case IResultSetHelper::ABSOLUTE: | 
|  | case IResultSetHelper::RELATIVE: | 
|  | if (nOffset > 0) | 
|  | m_nFilePos = nNumberOfRecords + 1; | 
|  | else if (nOffset < 0) | 
|  | m_nFilePos = 0; | 
|  | break; | 
|  | case IResultSetHelper::BOOKMARK: | 
|  | m_nFilePos = nTempPos;	 // vorherige Position | 
|  | } | 
|  | //	aStatus.Set(SDB_STAT_NO_DATA_FOUND); | 
|  | return sal_False; | 
|  |  | 
|  | End: | 
|  | nCurPos = m_nFilePos; | 
|  | return sal_True; | 
|  | } | 
|  | //------------------------------------------------------------------ | 
|  | sal_Bool OCalcTable::fetchRow( OValueRefRow& _rRow, const OSQLColumns & _rCols, | 
|  | sal_Bool _bUseTableDefs, sal_Bool bRetrieveData ) | 
|  | { | 
|  | RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::fetchRow" ); | 
|  | // read the bookmark | 
|  |  | 
|  | sal_Bool bIsCurRecordDeleted = sal_False; | 
|  | _rRow->setDeleted(bIsCurRecordDeleted); | 
|  | *(_rRow->get())[0] = m_nFilePos; | 
|  |  | 
|  | if (!bRetrieveData) | 
|  | return sal_True; | 
|  |  | 
|  | // fields | 
|  |  | 
|  | OSQLColumns::Vector::const_iterator aIter = _rCols.get().begin(); | 
|  | OSQLColumns::Vector::const_iterator aEnd = _rCols.get().end(); | 
|  | const OValueRefVector::Vector::size_type nCount = _rRow->get().size(); | 
|  | for (OValueRefVector::Vector::size_type i = 1; aIter != aEnd && i < nCount; | 
|  | ++aIter, i++) | 
|  | { | 
|  | if ( (_rRow->get())[i]->isBound() ) | 
|  | { | 
|  | sal_Int32 nType = 0; | 
|  | if ( _bUseTableDefs ) | 
|  | nType = m_aTypes[i-1]; | 
|  | else | 
|  | (*aIter)->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType; | 
|  |  | 
|  |  | 
|  | lcl_SetValue( (_rRow->get())[i]->get(), m_xSheet, m_nStartCol, m_nStartRow, m_bHasHeaders, | 
|  | m_aNullDate, m_nFilePos, i, nType ); | 
|  | } | 
|  | } | 
|  | return sal_True; | 
|  | } | 
|  | // ------------------------------------------------------------------------- | 
|  | void OCalcTable::FileClose() | 
|  | { | 
|  | RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::FileClose" ); | 
|  | ::osl::MutexGuard aGuard(m_aMutex); | 
|  |  | 
|  | OCalcTable_BASE::FileClose(); | 
|  | } | 
|  | // ------------------------------------------------------------------------- | 
|  |  |