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