1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_connectivity.hxx" 26 #include "calc/CTable.hxx" 27 #include <com/sun/star/sdbc/ColumnValue.hpp> 28 #include <com/sun/star/sdbc/DataType.hpp> 29 //#ifndef _COM_SUN_STAR_UCB_XCONTENTACCESS_HPP_ 30 //#include <com/sun/star/ucb/XContentAccess.hpp> 31 //#endif 32 #include <com/sun/star/sdbc/XRow.hpp> 33 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp> 34 #include <com/sun/star/sheet/XSpreadsheet.hpp> 35 #include <com/sun/star/sheet/XCellRangeAddressable.hpp> 36 #include <com/sun/star/sheet/XCellRangesQuery.hpp> 37 #include <com/sun/star/sheet/XDatabaseRanges.hpp> 38 #include <com/sun/star/sheet/XDatabaseRange.hpp> 39 #include <com/sun/star/sheet/XCellRangeReferrer.hpp> 40 #include <com/sun/star/sheet/XUsedAreaCursor.hpp> 41 #include <com/sun/star/sheet/CellFlags.hpp> 42 #include <com/sun/star/sheet/FormulaResult.hpp> 43 #include <com/sun/star/util/NumberFormat.hpp> 44 #include <com/sun/star/util/XNumberFormatsSupplier.hpp> 45 #include <com/sun/star/text/XText.hpp> 46 #include <svl/converter.hxx> 47 #include "calc/CConnection.hxx" 48 #include "calc/CColumns.hxx" 49 #include "connectivity/sdbcx/VColumn.hxx" 50 #include <rtl/ustrbuf.hxx> 51 #include <osl/thread.h> 52 #include <tools/config.hxx> 53 #include <comphelper/sequence.hxx> 54 #include <svl/zforlist.hxx> 55 #include <rtl/math.hxx> 56 #include <comphelper/extract.hxx> 57 #include <connectivity/dbexception.hxx> 58 #include <connectivity/dbconversion.hxx> 59 #include <comphelper/types.hxx> 60 #include <rtl/logfile.hxx> 61 62 using namespace connectivity; 63 using namespace connectivity::calc; 64 using namespace connectivity::file; 65 using namespace ::cppu; 66 using namespace ::dbtools; 67 using namespace ::com::sun::star::uno; 68 using namespace ::com::sun::star::beans; 69 using namespace ::com::sun::star::sdbcx; 70 using namespace ::com::sun::star::sdbc; 71 using namespace ::com::sun::star::container; 72 using namespace ::com::sun::star::lang; 73 using namespace ::com::sun::star::sheet; 74 using namespace ::com::sun::star::table; 75 using namespace ::com::sun::star::text; 76 using namespace ::com::sun::star::util; 77 78 // ------------------------------------------------------------------------- 79 80 void lcl_UpdateArea( const Reference<XCellRange>& xUsedRange, sal_Int32& rEndCol, sal_Int32& rEndRow ) 81 { 82 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_UpdateArea" ); 83 // update rEndCol, rEndRow if any non-empty cell in xUsedRange is right/below 84 85 const Reference<XCellRangesQuery> xUsedQuery( xUsedRange, UNO_QUERY ); 86 if ( xUsedQuery.is() ) 87 { 88 const sal_Int16 nContentFlags = 89 CellFlags::STRING | CellFlags::VALUE | CellFlags::DATETIME | CellFlags::FORMULA | CellFlags::ANNOTATION; 90 91 const Reference<XSheetCellRanges> xUsedRanges = xUsedQuery->queryContentCells( nContentFlags ); 92 const Sequence<CellRangeAddress> aAddresses = xUsedRanges->getRangeAddresses(); 93 94 const sal_Int32 nCount = aAddresses.getLength(); 95 const CellRangeAddress* pData = aAddresses.getConstArray(); 96 for ( sal_Int32 i=0; i<nCount; i++ ) 97 { 98 rEndCol = pData[i].EndColumn > rEndCol ? pData[i].EndColumn : rEndCol; 99 rEndRow = pData[i].EndRow > rEndRow ? pData[i].EndRow : rEndRow; 100 } 101 } 102 } 103 104 void lcl_GetDataArea( const Reference<XSpreadsheet>& xSheet, sal_Int32& rColumnCount, sal_Int32& rRowCount ) 105 { 106 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetDataArea" ); 107 Reference<XSheetCellCursor> xCursor = xSheet->createCursor(); 108 Reference<XCellRangeAddressable> xRange( xCursor, UNO_QUERY ); 109 if ( !xRange.is() ) 110 { 111 rColumnCount = rRowCount = 0; 112 return; 113 } 114 115 // first find the contiguous cell area starting at A1 116 117 xCursor->collapseToSize( 1, 1 ); // single (first) cell 118 xCursor->collapseToCurrentRegion(); // contiguous data area 119 120 CellRangeAddress aRegionAddr = xRange->getRangeAddress(); 121 sal_Int32 nEndCol = aRegionAddr.EndColumn; 122 sal_Int32 nEndRow = aRegionAddr.EndRow; 123 124 Reference<XUsedAreaCursor> xUsed( xCursor, UNO_QUERY ); 125 if ( xUsed.is() ) 126 { 127 // The used area from XUsedAreaCursor includes visible attributes. 128 // If the used area is larger than the contiguous cell area, find non-empty 129 // cells in that area. 130 131 xUsed->gotoEndOfUsedArea( sal_False ); 132 CellRangeAddress aUsedAddr = xRange->getRangeAddress(); 133 134 if ( aUsedAddr.EndColumn > aRegionAddr.EndColumn ) 135 { 136 Reference<XCellRange> xUsedRange = xSheet->getCellRangeByPosition( 137 aRegionAddr.EndColumn + 1, 0, aUsedAddr.EndColumn, aUsedAddr.EndRow ); 138 lcl_UpdateArea( xUsedRange, nEndCol, nEndRow ); 139 } 140 141 if ( aUsedAddr.EndRow > aRegionAddr.EndRow ) 142 { 143 // only up to the last column of aRegionAddr, the other columns are handled above 144 Reference<XCellRange> xUsedRange = xSheet->getCellRangeByPosition( 145 0, aRegionAddr.EndRow + 1, aRegionAddr.EndColumn, aUsedAddr.EndRow ); 146 lcl_UpdateArea( xUsedRange, nEndCol, nEndRow ); 147 } 148 } 149 150 rColumnCount = nEndCol + 1; // number of columns 151 rRowCount = nEndRow; // first row (headers) is not counted 152 } 153 154 CellContentType lcl_GetContentOrResultType( const Reference<XCell>& xCell ) 155 { 156 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetContentOrResultType" ); 157 CellContentType eCellType = xCell->getType(); 158 if ( eCellType == CellContentType_FORMULA ) 159 { 160 static const ::rtl::OUString s_sFormulaResultType(RTL_CONSTASCII_USTRINGPARAM("FormulaResultType")); 161 Reference<XPropertySet> xProp( xCell, UNO_QUERY ); 162 try 163 { 164 xProp->getPropertyValue( s_sFormulaResultType ) >>= eCellType; // type of formula result 165 } 166 catch (UnknownPropertyException&) 167 { 168 eCellType = CellContentType_VALUE; // if FormulaResultType property not available 169 } 170 } 171 return eCellType; 172 } 173 174 Reference<XCell> lcl_GetUsedCell( const Reference<XSpreadsheet>& xSheet, sal_Int32 nDocColumn, sal_Int32 nDocRow ) 175 { 176 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetUsedCell" ); 177 Reference<XCell> xCell = xSheet->getCellByPosition( nDocColumn, nDocRow ); 178 if ( xCell.is() && xCell->getType() == CellContentType_EMPTY ) 179 { 180 // get first non-empty cell 181 182 Reference<XCellRangeAddressable> xAddr( xSheet, UNO_QUERY ); 183 if (xAddr.is()) 184 { 185 CellRangeAddress aTotalRange = xAddr->getRangeAddress(); 186 sal_Int32 nLastRow = aTotalRange.EndRow; 187 Reference<XCellRangesQuery> xQuery( xSheet->getCellRangeByPosition( nDocColumn, nDocRow, nDocColumn, nLastRow ), UNO_QUERY ); 188 if (xQuery.is()) 189 { 190 // queryIntersection to get a ranges object 191 Reference<XSheetCellRanges> xRanges = xQuery->queryIntersection( aTotalRange ); 192 if (xRanges.is()) 193 { 194 Reference<XEnumerationAccess> xCells = xRanges->getCells(); 195 if (xCells.is()) 196 { 197 Reference<XEnumeration> xEnum = xCells->createEnumeration(); 198 if ( xEnum.is() && xEnum->hasMoreElements() ) 199 { 200 // get first non-empty cell from enumeration 201 xCell.set(xEnum->nextElement(),UNO_QUERY); 202 } 203 // otherwise, keep empty cell 204 } 205 } 206 } 207 } 208 } 209 return xCell; 210 } 211 212 bool lcl_HasTextInColumn( const Reference<XSpreadsheet>& xSheet, sal_Int32 nDocColumn, sal_Int32 nDocRow ) 213 { 214 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_HasTextInColumn" ); 215 // look for any text cell or text result in the column 216 217 Reference<XCellRangeAddressable> xAddr( xSheet, UNO_QUERY ); 218 if (xAddr.is()) 219 { 220 CellRangeAddress aTotalRange = xAddr->getRangeAddress(); 221 sal_Int32 nLastRow = aTotalRange.EndRow; 222 Reference<XCellRangesQuery> xQuery( xSheet->getCellRangeByPosition( nDocColumn, nDocRow, nDocColumn, nLastRow ), UNO_QUERY ); 223 if (xQuery.is()) 224 { 225 // are there text cells in the column? 226 Reference<XSheetCellRanges> xTextContent = xQuery->queryContentCells( CellFlags::STRING ); 227 if ( xTextContent.is() && xTextContent->hasElements() ) 228 return true; 229 230 // are there formulas with text results in the column? 231 Reference<XSheetCellRanges> xTextFormula = xQuery->queryFormulaCells( FormulaResult::STRING ); 232 if ( xTextFormula.is() && xTextFormula->hasElements() ) 233 return true; 234 } 235 } 236 237 return false; 238 } 239 240 void lcl_GetColumnInfo( const Reference<XSpreadsheet>& xSheet, const Reference<XNumberFormats>& xFormats, 241 sal_Int32 nDocColumn, sal_Int32 nStartRow, sal_Bool bHasHeaders, 242 ::rtl::OUString& rName, sal_Int32& rDataType, sal_Bool& rCurrency ) 243 { 244 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetColumnInfo" ); 245 //! avoid duplicate field names 246 247 // get column name from first row, if range contains headers 248 249 if ( bHasHeaders ) 250 { 251 Reference<XText> xHeaderText( xSheet->getCellByPosition( nDocColumn, nStartRow ), UNO_QUERY ); 252 if ( xHeaderText.is() ) 253 rName = xHeaderText->getString(); 254 } 255 256 // get column type from first data row 257 258 sal_Int32 nDataRow = nStartRow; 259 if ( bHasHeaders ) 260 ++nDataRow; 261 Reference<XCell> xDataCell = lcl_GetUsedCell( xSheet, nDocColumn, nDataRow ); 262 263 Reference<XPropertySet> xProp( xDataCell, UNO_QUERY ); 264 if ( xProp.is() ) 265 { 266 rCurrency = sal_False; // set to true for currency below 267 268 const CellContentType eCellType = lcl_GetContentOrResultType( xDataCell ); 269 // #i35178# use "text" type if there is any text cell in the column 270 if ( eCellType == CellContentType_TEXT || lcl_HasTextInColumn( xSheet, nDocColumn, nDataRow ) ) 271 rDataType = DataType::VARCHAR; 272 else if ( eCellType == CellContentType_VALUE ) 273 { 274 // get number format to distinguish between different types 275 276 sal_Int16 nNumType = NumberFormat::NUMBER; 277 try 278 { 279 static ::rtl::OUString s_NumberFormat(RTL_CONSTASCII_USTRINGPARAM("NumberFormat")); 280 sal_Int32 nKey = 0; 281 282 if ( xProp->getPropertyValue( s_NumberFormat ) >>= nKey ) 283 { 284 const Reference<XPropertySet> xFormat = xFormats->getByKey( nKey ); 285 if ( xFormat.is() ) 286 { 287 xFormat->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE) ) >>= nNumType; 288 } 289 } 290 } 291 catch ( Exception& ) 292 { 293 } 294 295 if ( nNumType & NumberFormat::TEXT ) 296 rDataType = DataType::VARCHAR; 297 else if ( nNumType & NumberFormat::NUMBER ) 298 rDataType = DataType::DECIMAL; 299 else if ( nNumType & NumberFormat::CURRENCY ) 300 { 301 rCurrency = sal_True; 302 rDataType = DataType::DECIMAL; 303 } 304 else if ( ( nNumType & NumberFormat::DATETIME ) == NumberFormat::DATETIME ) 305 { 306 // NumberFormat::DATETIME is DATE | TIME 307 rDataType = DataType::TIMESTAMP; 308 } 309 else if ( nNumType & NumberFormat::DATE ) 310 rDataType = DataType::DATE; 311 else if ( nNumType & NumberFormat::TIME ) 312 rDataType = DataType::TIME; 313 else if ( nNumType & NumberFormat::LOGICAL ) 314 rDataType = DataType::BIT; 315 else 316 rDataType = DataType::DECIMAL; 317 } 318 else 319 { 320 // whole column empty 321 rDataType = DataType::VARCHAR; 322 } 323 } 324 } 325 326 // ------------------------------------------------------------------------- 327 328 void lcl_SetValue( ORowSetValue& rValue, const Reference<XSpreadsheet>& xSheet, 329 sal_Int32 nStartCol, sal_Int32 nStartRow, sal_Bool bHasHeaders, 330 const ::Date& rNullDate, 331 sal_Int32 nDBRow, sal_Int32 nDBColumn, sal_Int32 nType ) 332 { 333 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_SetValue" ); 334 sal_Int32 nDocColumn = nStartCol + nDBColumn - 1; // database counts from 1 335 sal_Int32 nDocRow = nStartRow + nDBRow - 1; 336 if (bHasHeaders) 337 ++nDocRow; 338 339 const Reference<XCell> xCell = xSheet->getCellByPosition( nDocColumn, nDocRow ); 340 if ( xCell.is() ) 341 { 342 CellContentType eCellType = lcl_GetContentOrResultType( xCell ); 343 switch (nType) 344 { 345 case DataType::VARCHAR: 346 if ( eCellType == CellContentType_EMPTY ) 347 rValue.setNull(); 348 else 349 { 350 // #i25840# still let Calc convert numbers to text 351 const Reference<XText> xText( xCell, UNO_QUERY ); 352 if ( xText.is() ) 353 rValue = xText->getString(); 354 } 355 break; 356 case DataType::DECIMAL: 357 if ( eCellType == CellContentType_VALUE ) 358 rValue = xCell->getValue(); // double 359 else 360 rValue.setNull(); 361 break; 362 case DataType::BIT: 363 if ( eCellType == CellContentType_VALUE ) 364 rValue = (sal_Bool)( xCell->getValue() != 0.0 ); 365 else 366 rValue.setNull(); 367 break; 368 case DataType::DATE: 369 if ( eCellType == CellContentType_VALUE ) 370 { 371 ::Date aDate( rNullDate ); 372 aDate += (long)::rtl::math::approxFloor( xCell->getValue() ); 373 ::com::sun::star::util::Date aDateStruct( aDate.GetDay(), aDate.GetMonth(), aDate.GetYear() ); 374 rValue = aDateStruct; 375 } 376 else 377 rValue.setNull(); 378 break; 379 case DataType::TIME: 380 if ( eCellType == CellContentType_VALUE ) 381 { 382 double fCellVal = xCell->getValue(); 383 double fTime = fCellVal - rtl::math::approxFloor( fCellVal ); 384 long nIntTime = (long)rtl::math::round( fTime * 8640000.0 ); 385 if ( nIntTime == 8640000 ) 386 nIntTime = 0; // 23:59:59.995 and above is 00:00:00.00 387 ::com::sun::star::util::Time aTime; 388 aTime.HundredthSeconds = (sal_uInt16)( nIntTime % 100 ); 389 nIntTime /= 100; 390 aTime.Seconds = (sal_uInt16)( nIntTime % 60 ); 391 nIntTime /= 60; 392 aTime.Minutes = (sal_uInt16)( nIntTime % 60 ); 393 nIntTime /= 60; 394 OSL_ENSURE( nIntTime < 24, "error in time calculation" ); 395 aTime.Hours = (sal_uInt16) nIntTime; 396 rValue = aTime; 397 } 398 else 399 rValue.setNull(); 400 break; 401 case DataType::TIMESTAMP: 402 if ( eCellType == CellContentType_VALUE ) 403 { 404 double fCellVal = xCell->getValue(); 405 double fDays = ::rtl::math::approxFloor( fCellVal ); 406 double fTime = fCellVal - fDays; 407 long nIntDays = (long)fDays; 408 long nIntTime = (long)::rtl::math::round( fTime * 8640000.0 ); 409 if ( nIntTime == 8640000 ) 410 { 411 nIntTime = 0; // 23:59:59.995 and above is 00:00:00.00 412 ++nIntDays; // (next day) 413 } 414 415 ::com::sun::star::util::DateTime aDateTime; 416 417 aDateTime.HundredthSeconds = (sal_uInt16)( nIntTime % 100 ); 418 nIntTime /= 100; 419 aDateTime.Seconds = (sal_uInt16)( nIntTime % 60 ); 420 nIntTime /= 60; 421 aDateTime.Minutes = (sal_uInt16)( nIntTime % 60 ); 422 nIntTime /= 60; 423 OSL_ENSURE( nIntTime < 24, "error in time calculation" ); 424 aDateTime.Hours = (sal_uInt16) nIntTime; 425 426 ::Date aDate( rNullDate ); 427 aDate += nIntDays; 428 aDateTime.Day = aDate.GetDay(); 429 aDateTime.Month = aDate.GetMonth(); 430 aDateTime.Year = aDate.GetYear(); 431 432 rValue = aDateTime; 433 } 434 else 435 rValue.setNull(); 436 break; 437 } // switch (nType) 438 } 439 440 // rValue.setTypeKind(nType); 441 } 442 443 // ------------------------------------------------------------------------- 444 445 ::rtl::OUString lcl_GetColumnStr( sal_Int32 nColumn ) 446 { 447 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::lcl_GetColumnStr" ); 448 if ( nColumn < 26 ) 449 return ::rtl::OUString::valueOf( (sal_Unicode) ( 'A' + nColumn ) ); 450 else 451 { 452 ::rtl::OUStringBuffer aBuffer(2); 453 aBuffer.setLength( 2 ); 454 aBuffer.setCharAt( 0, (sal_Unicode) ( 'A' + ( nColumn / 26 ) - 1 ) ); 455 aBuffer.setCharAt( 1, (sal_Unicode) ( 'A' + ( nColumn % 26 ) ) ); 456 return aBuffer.makeStringAndClear(); 457 } 458 } 459 460 void OCalcTable::fillColumns() 461 { 462 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::fillColumns" ); 463 if ( !m_xSheet.is() ) 464 throw SQLException(); 465 466 String aStrFieldName; 467 aStrFieldName.AssignAscii("Column"); 468 ::rtl::OUString aTypeName; 469 ::comphelper::UStringMixEqual aCase(m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()); 470 const sal_Bool bStoresMixedCaseQuotedIdentifiers = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers(); 471 472 for (sal_Int32 i = 0; i < m_nDataCols; i++) 473 { 474 ::rtl::OUString aColumnName; 475 sal_Int32 eType = DataType::OTHER; 476 sal_Bool bCurrency = sal_False; 477 478 lcl_GetColumnInfo( m_xSheet, m_xFormats, m_nStartCol + i, m_nStartRow, m_bHasHeaders, 479 aColumnName, eType, bCurrency ); 480 481 if ( !aColumnName.getLength() ) 482 aColumnName = lcl_GetColumnStr( i ); 483 484 sal_Int32 nPrecision = 0; //! ... 485 sal_Int32 nDecimals = 0; //! ... 486 487 switch ( eType ) 488 { 489 case DataType::VARCHAR: 490 { 491 static const ::rtl::OUString s_sType(RTL_CONSTASCII_USTRINGPARAM("VARCHAR")); 492 aTypeName = s_sType; 493 } 494 break; 495 case DataType::DECIMAL: 496 aTypeName = ::rtl::OUString::createFromAscii("DECIMAL"); 497 break; 498 case DataType::BIT: 499 aTypeName = ::rtl::OUString::createFromAscii("BOOL"); 500 break; 501 case DataType::DATE: 502 aTypeName = ::rtl::OUString::createFromAscii("DATE"); 503 break; 504 case DataType::TIME: 505 aTypeName = ::rtl::OUString::createFromAscii("TIME"); 506 break; 507 case DataType::TIMESTAMP: 508 aTypeName = ::rtl::OUString::createFromAscii("TIMESTAMP"); 509 break; 510 default: 511 OSL_ASSERT("missing type name"); 512 aTypeName = ::rtl::OUString(); 513 } 514 515 // check if the column name already exists 516 ::rtl::OUString aAlias = aColumnName; 517 OSQLColumns::Vector::const_iterator aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase); 518 sal_Int32 nExprCnt = 0; 519 while(aFind != m_aColumns->get().end()) 520 { 521 (aAlias = aColumnName) += ::rtl::OUString::valueOf((sal_Int32)++nExprCnt); 522 aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase); 523 } 524 525 sdbcx::OColumn* pColumn = new sdbcx::OColumn( aAlias, aTypeName, ::rtl::OUString(),::rtl::OUString(), 526 ColumnValue::NULLABLE, nPrecision, nDecimals, 527 eType, sal_False, sal_False, bCurrency, 528 bStoresMixedCaseQuotedIdentifiers); 529 Reference< XPropertySet> xCol = pColumn; 530 m_aColumns->get().push_back(xCol); 531 m_aTypes.push_back(eType); 532 m_aPrecisions.push_back(nPrecision); 533 m_aScales.push_back(nDecimals); 534 } 535 } 536 537 // ------------------------------------------------------------------------- 538 OCalcTable::OCalcTable(sdbcx::OCollection* _pTables,OCalcConnection* _pConnection, 539 const ::rtl::OUString& _Name, 540 const ::rtl::OUString& _Type, 541 const ::rtl::OUString& _Description , 542 const ::rtl::OUString& _SchemaName, 543 const ::rtl::OUString& _CatalogName 544 ) : OCalcTable_BASE(_pTables,_pConnection,_Name, 545 _Type, 546 _Description, 547 _SchemaName, 548 _CatalogName) 549 ,m_pConnection(_pConnection) 550 ,m_nStartCol(0) 551 ,m_nStartRow(0) 552 ,m_nDataCols(0) 553 ,m_nDataRows(0) 554 ,m_bHasHeaders(sal_False) 555 { 556 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::OCalcTable" ); 557 } 558 // ----------------------------------------------------------------------------- 559 void OCalcTable::construct() 560 { 561 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::construct" ); 562 // get sheet object 563 Reference< XSpreadsheetDocument> xDoc = m_pConnection->acquireDoc(); 564 if (xDoc.is()) 565 { 566 Reference<XSpreadsheets> xSheets = xDoc->getSheets(); 567 if ( xSheets.is() && xSheets->hasByName( m_Name ) ) 568 { 569 m_xSheet.set(xSheets->getByName( m_Name ),UNO_QUERY); 570 if ( m_xSheet.is() ) 571 { 572 lcl_GetDataArea( m_xSheet, m_nDataCols, m_nDataRows ); 573 m_bHasHeaders = sal_True; 574 // whole sheet is always assumed to include a header row 575 } 576 } 577 else // no sheet -> try database range 578 { 579 Reference<XPropertySet> xDocProp( xDoc, UNO_QUERY ); 580 if ( xDocProp.is() ) 581 { 582 Reference<XDatabaseRanges> xRanges(xDocProp->getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("DatabaseRanges")) ),UNO_QUERY); 583 584 if ( xRanges.is() && xRanges->hasByName( m_Name ) ) 585 { 586 Reference<XDatabaseRange> xDBRange(xRanges->getByName( m_Name ),UNO_QUERY); 587 Reference<XCellRangeReferrer> xRefer( xDBRange, UNO_QUERY ); 588 if ( xRefer.is() ) 589 { 590 // Header flag is always stored with database range 591 // Get flag from FilterDescriptor 592 593 sal_Bool bRangeHeader = sal_True; 594 Reference<XPropertySet> xFiltProp( xDBRange->getFilterDescriptor(), UNO_QUERY ); 595 if ( xFiltProp.is() ) 596 xFiltProp->getPropertyValue(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ContainsHeader"))) >>= bRangeHeader; 597 598 Reference<XSheetCellRange> xSheetRange( xRefer->getReferredCells(), UNO_QUERY ); 599 Reference<XCellRangeAddressable> xAddr( xSheetRange, UNO_QUERY ); 600 if ( xSheetRange.is() && xAddr.is() ) 601 { 602 m_xSheet = xSheetRange->getSpreadsheet(); 603 CellRangeAddress aRangeAddr = xAddr->getRangeAddress(); 604 m_nStartCol = aRangeAddr.StartColumn; 605 m_nStartRow = aRangeAddr.StartRow; 606 m_nDataCols = aRangeAddr.EndColumn - m_nStartCol + 1; 607 // m_nDataRows is excluding header row 608 m_nDataRows = aRangeAddr.EndRow - m_nStartRow; 609 if ( !bRangeHeader ) 610 { 611 // m_nDataRows counts the whole range 612 m_nDataRows += 1; 613 } 614 615 m_bHasHeaders = bRangeHeader; 616 } 617 } 618 } 619 } 620 } 621 622 Reference<XNumberFormatsSupplier> xSupp( xDoc, UNO_QUERY ); 623 if (xSupp.is()) 624 m_xFormats = xSupp->getNumberFormats(); 625 626 Reference<XPropertySet> xProp( xDoc, UNO_QUERY ); 627 if (xProp.is()) 628 { 629 ::com::sun::star::util::Date aDateStruct; 630 if ( xProp->getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("NullDate")) ) >>= aDateStruct ) 631 m_aNullDate = ::Date( aDateStruct.Day, aDateStruct.Month, aDateStruct.Year ); 632 } 633 } 634 635 //! default if no null date available? 636 637 fillColumns(); 638 639 refreshColumns(); 640 } 641 // ------------------------------------------------------------------------- 642 void OCalcTable::refreshColumns() 643 { 644 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::refreshColumns" ); 645 ::osl::MutexGuard aGuard( m_aMutex ); 646 647 TStringVector aVector; 648 649 OSQLColumns::Vector::const_iterator aEnd = m_aColumns->get().end(); 650 for(OSQLColumns::Vector::const_iterator aIter = m_aColumns->get().begin();aIter != aEnd;++aIter) 651 aVector.push_back(Reference< XNamed>(*aIter,UNO_QUERY)->getName()); 652 653 if(m_pColumns) 654 m_pColumns->reFill(aVector); 655 else 656 m_pColumns = new OCalcColumns(this,m_aMutex,aVector); 657 } 658 // ------------------------------------------------------------------------- 659 void OCalcTable::refreshIndexes() 660 { 661 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::refreshIndexes" ); 662 // Calc table has no index 663 } 664 665 // ------------------------------------------------------------------------- 666 void SAL_CALL OCalcTable::disposing(void) 667 { 668 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::disposing" ); 669 OFileTable::disposing(); 670 ::osl::MutexGuard aGuard(m_aMutex); 671 m_aColumns = NULL; 672 if ( m_pConnection ) 673 m_pConnection->releaseDoc(); 674 m_pConnection = NULL; 675 676 } 677 // ------------------------------------------------------------------------- 678 Sequence< Type > SAL_CALL OCalcTable::getTypes( ) throw(RuntimeException) 679 { 680 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::getTypes" ); 681 Sequence< Type > aTypes = OTable_TYPEDEF::getTypes(); 682 ::std::vector<Type> aOwnTypes; 683 aOwnTypes.reserve(aTypes.getLength()); 684 685 const Type* pBegin = aTypes.getConstArray(); 686 const Type* pEnd = pBegin + aTypes.getLength(); 687 for(;pBegin != pEnd;++pBegin) 688 { 689 if(!( *pBegin == ::getCppuType((const Reference<XKeysSupplier>*)0) || 690 *pBegin == ::getCppuType((const Reference<XIndexesSupplier>*)0) || 691 *pBegin == ::getCppuType((const Reference<XRename>*)0) || 692 *pBegin == ::getCppuType((const Reference<XAlterTable>*)0) || 693 *pBegin == ::getCppuType((const Reference<XDataDescriptorFactory>*)0))) 694 aOwnTypes.push_back(*pBegin); 695 } 696 aOwnTypes.push_back(::getCppuType( (const Reference< ::com::sun::star::lang::XUnoTunnel > *)0 )); 697 698 const Type* pAttrs = aOwnTypes.empty() ? 0 : &aOwnTypes[0]; 699 return Sequence< Type >(pAttrs, aOwnTypes.size()); 700 } 701 702 // ------------------------------------------------------------------------- 703 Any SAL_CALL OCalcTable::queryInterface( const Type & rType ) throw(RuntimeException) 704 { 705 if( rType == ::getCppuType((const Reference<XKeysSupplier>*)0) || 706 rType == ::getCppuType((const Reference<XIndexesSupplier>*)0) || 707 rType == ::getCppuType((const Reference<XRename>*)0) || 708 rType == ::getCppuType((const Reference<XAlterTable>*)0) || 709 rType == ::getCppuType((const Reference<XDataDescriptorFactory>*)0)) 710 return Any(); 711 712 const Any aRet = ::cppu::queryInterface(rType,static_cast< ::com::sun::star::lang::XUnoTunnel*> (this)); 713 return aRet.hasValue() ? aRet : OTable_TYPEDEF::queryInterface(rType); 714 } 715 716 //-------------------------------------------------------------------------- 717 Sequence< sal_Int8 > OCalcTable::getUnoTunnelImplementationId() 718 { 719 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::getUnoTunnelImplementationId" ); 720 static ::cppu::OImplementationId * pId = 0; 721 if (! pId) 722 { 723 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); 724 if (! pId) 725 { 726 static ::cppu::OImplementationId aId; 727 pId = &aId; 728 } 729 } 730 return pId->getImplementationId(); 731 } 732 733 // com::sun::star::lang::XUnoTunnel 734 //------------------------------------------------------------------ 735 sal_Int64 OCalcTable::getSomething( const Sequence< sal_Int8 > & rId ) throw (RuntimeException) 736 { 737 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::getSomething" ); 738 return (rId.getLength() == 16 && 0 == rtl_compareMemory(getUnoTunnelImplementationId().getConstArray(), rId.getConstArray(), 16 ) ) 739 ? reinterpret_cast< sal_Int64 >( this ) 740 : OCalcTable_BASE::getSomething(rId); 741 } 742 //------------------------------------------------------------------ 743 sal_Int32 OCalcTable::getCurrentLastPos() const 744 { 745 //RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::getCurrentLastPos" ); 746 return m_nDataRows; 747 } 748 //------------------------------------------------------------------ 749 sal_Bool OCalcTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos) 750 { 751 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::seekRow" ); 752 // ---------------------------------------------------------- 753 // Positionierung vorbereiten: 754 755 sal_uInt32 nNumberOfRecords = m_nDataRows; 756 sal_uInt32 nTempPos = m_nFilePos; 757 m_nFilePos = nCurPos; 758 759 switch(eCursorPosition) 760 { 761 case IResultSetHelper::NEXT: 762 m_nFilePos++; 763 break; 764 case IResultSetHelper::PRIOR: 765 if (m_nFilePos > 0) 766 m_nFilePos--; 767 break; 768 case IResultSetHelper::FIRST: 769 m_nFilePos = 1; 770 break; 771 case IResultSetHelper::LAST: 772 m_nFilePos = nNumberOfRecords; 773 break; 774 case IResultSetHelper::RELATIVE: 775 m_nFilePos = (((sal_Int32)m_nFilePos) + nOffset < 0) ? 0L 776 : (sal_uInt32)(((sal_Int32)m_nFilePos) + nOffset); 777 break; 778 case IResultSetHelper::ABSOLUTE: 779 case IResultSetHelper::BOOKMARK: 780 m_nFilePos = (sal_uInt32)nOffset; 781 break; 782 } 783 784 if (m_nFilePos > (sal_Int32)nNumberOfRecords) 785 m_nFilePos = (sal_Int32)nNumberOfRecords + 1; 786 787 if (m_nFilePos == 0 || m_nFilePos == (sal_Int32)nNumberOfRecords + 1) 788 goto Error; 789 else 790 { 791 //! read buffer / setup row object etc? 792 } 793 goto End; 794 795 Error: 796 switch(eCursorPosition) 797 { 798 case IResultSetHelper::PRIOR: 799 case IResultSetHelper::FIRST: 800 m_nFilePos = 0; 801 break; 802 case IResultSetHelper::LAST: 803 case IResultSetHelper::NEXT: 804 case IResultSetHelper::ABSOLUTE: 805 case IResultSetHelper::RELATIVE: 806 if (nOffset > 0) 807 m_nFilePos = nNumberOfRecords + 1; 808 else if (nOffset < 0) 809 m_nFilePos = 0; 810 break; 811 case IResultSetHelper::BOOKMARK: 812 m_nFilePos = nTempPos; // vorherige Position 813 } 814 // aStatus.Set(SDB_STAT_NO_DATA_FOUND); 815 return sal_False; 816 817 End: 818 nCurPos = m_nFilePos; 819 return sal_True; 820 } 821 //------------------------------------------------------------------ 822 sal_Bool OCalcTable::fetchRow( OValueRefRow& _rRow, const OSQLColumns & _rCols, 823 sal_Bool _bUseTableDefs, sal_Bool bRetrieveData ) 824 { 825 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::fetchRow" ); 826 // read the bookmark 827 828 sal_Bool bIsCurRecordDeleted = sal_False; 829 _rRow->setDeleted(bIsCurRecordDeleted); 830 *(_rRow->get())[0] = m_nFilePos; 831 832 if (!bRetrieveData) 833 return sal_True; 834 835 // fields 836 837 OSQLColumns::Vector::const_iterator aIter = _rCols.get().begin(); 838 OSQLColumns::Vector::const_iterator aEnd = _rCols.get().end(); 839 const OValueRefVector::Vector::size_type nCount = _rRow->get().size(); 840 for (OValueRefVector::Vector::size_type i = 1; aIter != aEnd && i < nCount; 841 ++aIter, i++) 842 { 843 if ( (_rRow->get())[i]->isBound() ) 844 { 845 sal_Int32 nType = 0; 846 if ( _bUseTableDefs ) 847 nType = m_aTypes[i-1]; 848 else 849 (*aIter)->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType; 850 851 852 lcl_SetValue( (_rRow->get())[i]->get(), m_xSheet, m_nStartCol, m_nStartRow, m_bHasHeaders, 853 m_aNullDate, m_nFilePos, i, nType ); 854 } 855 } 856 return sal_True; 857 } 858 // ------------------------------------------------------------------------- 859 void OCalcTable::FileClose() 860 { 861 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "calc", "Ocke.Janssen@sun.com", "OCalcTable::FileClose" ); 862 ::osl::MutexGuard aGuard(m_aMutex); 863 864 OCalcTable_BASE::FileClose(); 865 } 866 // ------------------------------------------------------------------------- 867 868