1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dpcachetable.cxx,v $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 #include "dpcachetable.hxx"
35 #include "document.hxx"
36 #include "address.hxx"
38 #include "dptabdat.hxx"
39 #include "dptabsrc.hxx"
40 #include "dpobject.hxx"
42 #include <com/sun/star/i18n/LocaleDataItem.hpp>
43 #include <com/sun/star/sdbc/DataType.hpp>
44 #include <com/sun/star/sdbc/XRow.hpp>
45 #include <com/sun/star/sdbc/XRowSet.hpp>
46 #include <com/sun/star/sdbc/XResultSetMetaData.hpp>
47 #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
48 #include <com/sun/star/util/Date.hpp>
49 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
50 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
54 using namespace ::com::sun::star
;
56 using ::rtl::OUString
;
59 using ::std::hash_map
;
60 using ::std::hash_set
;
61 using ::std::auto_ptr
;
62 using ::com::sun::star::i18n::LocaleDataItem
;
63 using ::com::sun::star::uno::Exception
;
64 using ::com::sun::star::uno::Reference
;
65 using ::com::sun::star::uno::Sequence
;
66 using ::com::sun::star::uno::Any
;
67 using ::com::sun::star::uno::UNO_QUERY
;
68 using ::com::sun::star::uno::UNO_QUERY_THROW
;
69 using ::com::sun::star::sheet::DataPilotFieldFilter
;
71 const double D_TIMEFACTOR
= 86400.0;
73 static BOOL
lcl_HasQueryEntry( const ScQueryParam
& rParam
)
75 return rParam
.GetEntryCount() > 0 &&
76 rParam
.GetEntry(0).bDoQuery
;
79 // ----------------------------------------------------------------------------
81 static ScDPCacheCell EmptyCellContent
= ScDPCacheCell();
83 // ----------------------------------------------------------------------------
85 ScDPCacheTable::Cell::Cell() :
91 ScDPCacheTable::Cell::~Cell()
95 // ----------------------------------------------------------------------------
97 ScDPCacheTable::FilterItem::FilterItem() :
98 mnMatchStrId(ScSimpleSharedString::EMPTY
),
104 // ----------------------------------------------------------------------------
106 ScDPCacheTable::SingleFilter::SingleFilter(ScSimpleSharedString
& rSharedString
,
107 sal_Int32 nMatchStrId
, double fValue
, bool bHasValue
) :
108 mrSharedString(rSharedString
)
110 maItem
.mnMatchStrId
= nMatchStrId
;
111 maItem
.mfValue
= fValue
;
112 maItem
.mbHasValue
= bHasValue
;
115 bool ScDPCacheTable::SingleFilter::match(const ScDPCacheCell
& rCell
) const
117 if (rCell
.mnStrId
!= maItem
.mnMatchStrId
&&
118 (!rCell
.mbNumeric
|| rCell
.mfValue
!= maItem
.mfValue
))
124 const String
ScDPCacheTable::SingleFilter::getMatchString()
126 const String
* pStr
= mrSharedString
.getString(maItem
.mnMatchStrId
);
133 double ScDPCacheTable::SingleFilter::getMatchValue() const
135 return maItem
.mfValue
;
138 bool ScDPCacheTable::SingleFilter::hasValue() const
140 return maItem
.mbHasValue
;
143 // ----------------------------------------------------------------------------
145 ScDPCacheTable::GroupFilter::GroupFilter(ScSimpleSharedString
& rSharedString
) :
146 mrSharedString(rSharedString
)
150 bool ScDPCacheTable::GroupFilter::match(const ScDPCacheCell
& rCell
) const
152 vector
<FilterItem
>::const_iterator itrEnd
= maItems
.end();
153 for (vector
<FilterItem
>::const_iterator itr
= maItems
.begin(); itr
!= itrEnd
; ++itr
)
157 bMatch
= (itr
->mfValue
== rCell
.mfValue
);
159 bMatch
= (itr
->mnMatchStrId
== rCell
.mnStrId
);
167 void ScDPCacheTable::GroupFilter::addMatchItem(const String
& rStr
, double fVal
, bool bHasValue
)
169 sal_Int32 nStrId
= mrSharedString
.getStringId(rStr
);
171 aItem
.mnMatchStrId
= nStrId
;
172 aItem
.mfValue
= fVal
;
173 aItem
.mbHasValue
= bHasValue
;
174 maItems
.push_back(aItem
);
177 size_t ScDPCacheTable::GroupFilter::getMatchItemCount() const
179 return maItems
.size();
182 // ----------------------------------------------------------------------------
184 ScDPCacheTable::Criterion::Criterion() :
186 mpFilter(static_cast<FilterBase
*>(NULL
))
190 // ----------------------------------------------------------------------------
192 ScDPCacheTable::ScDPCacheTable(ScDPCollection
* pCollection
) :
193 mrSharedString(pCollection
->GetSharedString()),
194 mpCollection(pCollection
)
198 ScDPCacheTable::~ScDPCacheTable()
202 sal_Int32
ScDPCacheTable::getRowSize() const
204 return maTable
.size();
207 sal_Int32
ScDPCacheTable::getColSize() const
209 return maTable
.empty() ? 0 : maTable
[0].size();
212 void ScDPCacheTable::fillTable(ScDocument
* pDoc
, const ScRange
& rRange
, const ScQueryParam
& rQuery
, BOOL
* pSpecial
,
213 bool bIgnoreEmptyRows
)
215 SCTAB nTab
= rRange
.aStart
.Tab();
216 SCCOL nStartCol
= rRange
.aStart
.Col();
217 SCROW nStartRow
= rRange
.aStart
.Row();
218 SCCOL nColCount
= rRange
.aEnd
.Col() - rRange
.aStart
.Col() + 1;
219 SCROW nRowCount
= rRange
.aEnd
.Row() - rRange
.aStart
.Row() + 1;
221 if (nRowCount
<= 1 || nColCount
<= 0)
225 maTable
.reserve(nRowCount
);
227 maHeader
.reserve(nColCount
);
228 maRowsVisible
.clear();
229 maRowsVisible
.reserve(nRowCount
);
232 for (SCCOL nCol
= 0; nCol
< nColCount
; ++nCol
)
235 pDoc
->GetString(nCol
+ nStartCol
, nStartRow
, nTab
, aStr
);
236 sal_Int32 nStrId
= mrSharedString
.insertString(aStr
);
237 maHeader
.push_back(nStrId
);
240 // Initialize field entries container.
241 maFieldEntries
.clear();
242 maFieldEntries
.reserve(nColCount
);
243 for (SCCOL nCol
= 0; nCol
< nColCount
; ++nCol
)
245 TypedScStrCollectionPtr
p(new TypedScStrCollection
);
246 maFieldEntries
.push_back(p
);
249 vector
<SCROW
> aLastNonEmptyRows(nColCount
, 0);
252 for (SCROW nRow
= 1; nRow
< nRowCount
; ++nRow
)
254 if ( lcl_HasQueryEntry(rQuery
) && !pDoc
->ValidQuery(nRow
+ nStartRow
, nTab
, rQuery
, pSpecial
) )
255 // filtered out by standard filter.
258 if ( bIgnoreEmptyRows
&&
259 pDoc
->IsBlockEmpty(nTab
, nStartCol
, nRow
+ nStartRow
,
260 nStartCol
+ nColCount
- 1, nRow
+ nStartRow
) )
261 // skip an empty row.
264 // Insert a new row into cache table.
265 maRowsVisible
.push_back(true);
266 maTable
.push_back( vector
<Cell
>() );
267 maTable
.back().reserve(nColCount
);
269 for (SCCOL nCol
= 0; nCol
< nColCount
; ++nCol
)
271 maTable
.back().push_back( ScDPCacheTable::Cell() );
272 Cell
& rCell
= maTable
.back().back();
273 rCell
.mnCategoryRef
= maTable
.size()-1;
276 bool bReadCell
= nRow
== 0 || pDoc
->HasData(nStartCol
+ nCol
, nStartRow
+ nRow
, nTab
);
279 aLastNonEmptyRows
[nCol
] = maTable
.size()-1;
281 pDoc
->GetString(nStartCol
+ nCol
, nStartRow
+ nRow
, nTab
, aCellStr
);
282 aCell
.mnStrId
= mrSharedString
.insertString(aCellStr
);
283 aCell
.mnType
= SC_VALTYPE_STRING
;
284 aCell
.mbNumeric
= false;
285 ScAddress
aPos(nStartCol
+ nCol
, nStartRow
+ nRow
, nTab
);
286 getValueData(pDoc
, aPos
, aCell
);
287 rCell
.mpContent
= mpCollection
->getCacheCellFromPool(aCell
);
290 rCell
.mnCategoryRef
= aLastNonEmptyRows
[nCol
];
293 if (rCell
.mpContent
&& rCell
.mpContent
->mbNumeric
)
294 pNew
= new TypedStrData(aCellStr
, rCell
.mpContent
->mfValue
, SC_STRTYPE_VALUE
);
296 pNew
= new TypedStrData(aCellStr
);
298 if (!maFieldEntries
[nCol
]->Insert(pNew
))
304 void lcl_GetCellValue(const Reference
<sdbc::XRow
>& xRow
, sal_Int32 nType
, long nCol
,
305 const Date
& rNullDate
, ScDPCacheCell
& rCell
, String
& rStr
,
306 ScSimpleSharedString
& rSharedString
)
308 short nNumType
= NUMBERFORMAT_NUMBER
;
309 BOOL bEmptyFlag
= FALSE
;
312 rStr
= xRow
->getString(nCol
);
313 rCell
.mnStrId
= rSharedString
.getStringId(rStr
);
314 rCell
.mnType
= SC_VALTYPE_STRING
;
318 case sdbc::DataType::BIT
:
319 case sdbc::DataType::BOOLEAN
:
321 nNumType
= NUMBERFORMAT_LOGICAL
;
322 rCell
.mfValue
= xRow
->getBoolean(nCol
) ? 1 : 0;
323 bEmptyFlag
= (rCell
.mfValue
== 0.0 && xRow
->wasNull());
324 rCell
.mbNumeric
= true;
325 rCell
.mnType
= SC_VALTYPE_VALUE
;
329 case sdbc::DataType::TINYINT
:
330 case sdbc::DataType::SMALLINT
:
331 case sdbc::DataType::INTEGER
:
332 case sdbc::DataType::BIGINT
:
333 case sdbc::DataType::FLOAT
:
334 case sdbc::DataType::REAL
:
335 case sdbc::DataType::DOUBLE
:
336 case sdbc::DataType::NUMERIC
:
337 case sdbc::DataType::DECIMAL
:
339 //! do the conversion here?
340 rCell
.mfValue
= xRow
->getDouble(nCol
);
341 bEmptyFlag
= (rCell
.mfValue
== 0.0 && xRow
->wasNull());
342 rCell
.mbNumeric
= true;
343 rCell
.mnType
= SC_VALTYPE_VALUE
;
347 case sdbc::DataType::CHAR
:
348 case sdbc::DataType::VARCHAR
:
349 case sdbc::DataType::LONGVARCHAR
:
350 bEmptyFlag
= (rStr
.Len() == 0 && xRow
->wasNull());
353 case sdbc::DataType::DATE
:
355 nNumType
= NUMBERFORMAT_DATE
;
357 util::Date aDate
= xRow
->getDate(nCol
);
358 rCell
.mfValue
= Date(aDate
.Day
, aDate
.Month
, aDate
.Year
) - rNullDate
;
359 bEmptyFlag
= xRow
->wasNull();
360 rCell
.mbNumeric
= true;
361 rCell
.mnType
= SC_VALTYPE_VALUE
;
365 case sdbc::DataType::TIME
:
367 nNumType
= NUMBERFORMAT_TIME
;
369 util::Time aTime
= xRow
->getTime(nCol
);
370 rCell
.mfValue
= ( aTime
.Hours
* 3600 + aTime
.Minutes
* 60 +
371 aTime
.Seconds
+ aTime
.HundredthSeconds
/ 100.0 ) / D_TIMEFACTOR
;
372 bEmptyFlag
= xRow
->wasNull();
373 rCell
.mbNumeric
= true;
374 rCell
.mnType
= SC_VALTYPE_VALUE
;
378 case sdbc::DataType::TIMESTAMP
:
380 nNumType
= NUMBERFORMAT_DATETIME
;
382 util::DateTime aStamp
= xRow
->getTimestamp(nCol
);
383 rCell
.mfValue
= ( Date( aStamp
.Day
, aStamp
.Month
, aStamp
.Year
) - rNullDate
) +
384 ( aStamp
.Hours
* 3600 + aStamp
.Minutes
* 60 +
385 aStamp
.Seconds
+ aStamp
.HundredthSeconds
/ 100.0 ) / D_TIMEFACTOR
;
386 bEmptyFlag
= xRow
->wasNull();
387 rCell
.mbNumeric
= true;
388 rCell
.mnType
= SC_VALTYPE_VALUE
;
392 case sdbc::DataType::SQLNULL
:
393 case sdbc::DataType::BINARY
:
394 case sdbc::DataType::VARBINARY
:
395 case sdbc::DataType::LONGVARBINARY
:
400 catch (uno::Exception
&)
405 void ScDPCacheTable::fillTable(const Reference
<sdbc::XRowSet
>& xRowSet
, const Date
& rNullDate
)
408 // Dont' even waste time to go any further.
413 Reference
<sdbc::XResultSetMetaDataSupplier
> xMetaSupp(xRowSet
, UNO_QUERY_THROW
);
414 Reference
<sdbc::XResultSetMetaData
> xMeta
= xMetaSupp
->getMetaData();
418 sal_Int32 nColCount
= xMeta
->getColumnCount();
420 // Get column titles and types.
421 vector
<sal_Int32
> aColTypes(nColCount
);
423 maHeader
.reserve(nColCount
);
424 for (sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
426 String aColTitle
= xMeta
->getColumnLabel(nCol
+1);
427 aColTypes
[nCol
] = xMeta
->getColumnType(nCol
+1);
428 maHeader
.push_back( mrSharedString
.getStringId(aColTitle
) );
431 // Initialize field entries container.
432 maFieldEntries
.clear();
433 maFieldEntries
.reserve(nColCount
);
434 for (SCCOL nCol
= 0; nCol
< nColCount
; ++nCol
)
436 TypedScStrCollectionPtr
p(new TypedScStrCollection
);
437 maFieldEntries
.push_back(p
);
440 // Now get the data rows.
441 Reference
<sdbc::XRow
> xRow(xRowSet
, UNO_QUERY_THROW
);
444 maRowsVisible
.clear();
447 maRowsVisible
.push_back(true);
448 maTable
.push_back( vector
<Cell
>() );
449 maTable
.back().reserve(nColCount
);
450 for (sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
452 maTable
.back().push_back( Cell() );
453 Cell
& rCell
= maTable
.back().back();
454 ScDPCacheCell aCellContent
;
456 lcl_GetCellValue(xRow
, aColTypes
[nCol
], nCol
+1, rNullDate
, aCellContent
, aStr
, mrSharedString
);
457 rCell
.mpContent
= mpCollection
->getCacheCellFromPool(aCellContent
);
460 if (rCell
.mpContent
->mbNumeric
)
461 pNew
= new TypedStrData(aStr
, rCell
.mpContent
->mfValue
, SC_STRTYPE_VALUE
);
463 pNew
= new TypedStrData(aStr
);
465 if (!maFieldEntries
[nCol
]->Insert(pNew
))
469 while (xRowSet
->next());
471 xRowSet
->beforeFirst();
473 catch (const Exception
&)
478 bool ScDPCacheTable::isRowActive(sal_Int32 nRow
) const
480 if (nRow
< 0 || static_cast<size_t>(nRow
) >= maRowsVisible
.size())
481 // row index out of bound
484 return maRowsVisible
[nRow
];
487 void ScDPCacheTable::filterByPageDimension(const vector
<Criterion
>& rCriteria
, const hash_set
<sal_Int32
>& rRepeatIfEmptyDims
)
489 sal_Int32 nRowSize
= getRowSize();
490 if (nRowSize
!= static_cast<sal_Int32
>(maRowsVisible
.size()))
492 // sizes of the two tables differ!
496 for (sal_Int32 nRow
= 0; nRow
< nRowSize
; ++nRow
)
497 maRowsVisible
[nRow
] = isRowQualified(nRow
, rCriteria
, rRepeatIfEmptyDims
);
500 const ScDPCacheCell
* ScDPCacheTable::getCell(SCCOL nCol
, SCROW nRow
, bool bRepeatIfEmpty
) const
502 if ( nRow
>= static_cast<SCROW
>(maTable
.size()) )
505 const vector
<Cell
>& rRow
= maTable
[nRow
];
506 if ( nCol
< 0 || static_cast<size_t>(nCol
) >= rRow
.size() )
509 const Cell
& rCell
= rRow
[nCol
];
510 const ScDPCacheCell
* pCell
= rCell
.mpContent
;
511 if (bRepeatIfEmpty
&& !pCell
)
512 pCell
= getCell(nCol
, rCell
.mnCategoryRef
, false);
514 return pCell
? pCell
: &EmptyCellContent
;
517 const String
* ScDPCacheTable::getFieldName(sal_Int32 nIndex
) const
519 if (nIndex
>= static_cast<sal_Int32
>(maHeader
.size()))
522 return mrSharedString
.getString(maHeader
[nIndex
]);
525 const TypedScStrCollection
& ScDPCacheTable::getFieldEntries(sal_Int32 nIndex
) const
527 if (nIndex
< 0 || static_cast<size_t>(nIndex
) >= maFieldEntries
.size())
529 // index out of bound. Hopefully this code will never be reached.
530 static const TypedScStrCollection emptyCollection
;
531 return emptyCollection
;
534 return *maFieldEntries
[nIndex
].get();
537 void ScDPCacheTable::filterTable(const vector
<Criterion
>& rCriteria
, Sequence
< Sequence
<Any
> >& rTabData
,
538 const hash_set
<sal_Int32
>& rRepeatIfEmptyDims
)
540 sal_Int32 nRowSize
= getRowSize();
541 sal_Int32 nColSize
= getColSize();
544 // no data to filter.
547 // Row first, then column.
548 vector
< Sequence
<Any
> > tableData
;
549 tableData
.reserve(nRowSize
+1);
552 Sequence
<Any
> headerRow(nColSize
);
553 for (sal_Int32 nCol
= 0; nCol
< nColSize
; ++nCol
)
556 const String
* pStr
= mrSharedString
.getString(maHeader
[nCol
]);
562 headerRow
[nCol
] = any
;
564 tableData
.push_back(headerRow
);
567 for (sal_Int32 nRow
= 0; nRow
< nRowSize
; ++nRow
)
569 if (!maRowsVisible
[nRow
])
570 // This row is filtered out.
573 if (!isRowQualified(nRow
, rCriteria
, rRepeatIfEmptyDims
))
576 // Insert this row into table.
578 Sequence
<Any
> row(nColSize
);
579 for (SCCOL nCol
= 0; nCol
< nColSize
; ++nCol
)
582 bool bRepeatIfEmpty
= rRepeatIfEmptyDims
.count(nCol
) > 0;
583 const ScDPCacheCell
* pCell
= getCell(nCol
, nRow
, bRepeatIfEmpty
);
586 // This should never happen, but in case this happens, just
587 // stick in an empty string.
594 if (pCell
->mbNumeric
)
595 any
<<= pCell
->mfValue
;
599 const String
* pStr
= mrSharedString
.getString(pCell
->mnStrId
);
606 tableData
.push_back(row
);
609 // convert vector to Seqeunce
610 sal_Int32 nTabSize
= static_cast<sal_Int32
>(tableData
.size());
611 rTabData
.realloc(nTabSize
);
612 for (sal_Int32 i
= 0; i
< nTabSize
; ++i
)
613 rTabData
[i
] = tableData
[i
];
616 void ScDPCacheTable::clear()
620 maFieldEntries
.clear();
621 maRowsVisible
.clear();
624 bool ScDPCacheTable::empty() const
626 return maTable
.empty();
629 bool ScDPCacheTable::isRowQualified(sal_Int32 nRow
, const vector
<Criterion
>& rCriteria
,
630 const hash_set
<sal_Int32
>& rRepeatIfEmptyDims
) const
632 sal_Int32 nColSize
= getColSize();
633 vector
<Criterion
>::const_iterator itrEnd
= rCriteria
.end();
634 for (vector
<Criterion
>::const_iterator itr
= rCriteria
.begin(); itr
!= itrEnd
; ++itr
)
636 if (itr
->mnFieldIndex
>= nColSize
)
637 // specified field is outside the source data columns. Don't
638 // use this criterion.
641 // Check if the 'repeat if empty' flag is set for this field.
642 bool bRepeatIfEmpty
= rRepeatIfEmptyDims
.count(itr
->mnFieldIndex
) > 0;
643 const ScDPCacheCell
* pCell
= getCell(static_cast<SCCOL
>(itr
->mnFieldIndex
), nRow
, bRepeatIfEmpty
);
645 // This should never happen, but just in case...
648 if (!itr
->mpFilter
->match(*pCell
))
654 void ScDPCacheTable::getValueData(ScDocument
* pDoc
, const ScAddress
& rPos
, ScDPCacheCell
& rCell
)
656 ScBaseCell
* pCell
= pDoc
->GetCell(rPos
);
659 rCell
.mnType
= SC_VALTYPE_EMPTY
;
663 CellType eType
= pCell
->GetCellType();
664 if (eType
== CELLTYPE_NOTE
)
667 rCell
.mnType
= SC_VALTYPE_EMPTY
;
671 if (eType
== CELLTYPE_FORMULA
&& static_cast<ScFormulaCell
*>(pCell
)->GetErrCode())
673 // formula cell with error
674 rCell
.mnType
= SC_VALTYPE_ERROR
;
678 if ( pCell
->HasValueData() )
680 if (eType
== CELLTYPE_VALUE
)
682 rCell
.mfValue
= static_cast<ScValueCell
*>(pCell
)->GetValue();
683 else if (eType
== CELLTYPE_FORMULA
)
685 rCell
.mfValue
= static_cast<ScFormulaCell
*>(pCell
)->GetValue();
687 rCell
.mbNumeric
= true;
688 rCell
.mnType
= SC_VALTYPE_VALUE
;