1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "dpcache.hxx"
22 #include "document.hxx"
23 #include "queryentry.hxx"
24 #include "queryparam.hxx"
25 #include "dpglobal.hxx"
26 #include "dpobject.hxx"
27 #include "globstr.hrc"
28 #include "docoptio.hxx"
29 #include "dpitemdata.hxx"
31 #include "dpnumgroupinfo.hxx"
33 #include <rtl/math.hxx>
34 #include <unotools/textsearch.hxx>
35 #include <unotools/localedatawrapper.hxx>
36 #include <svl/zforlist.hxx>
39 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
42 using namespace ::com::sun::star
;
44 using ::com::sun::star::uno::Exception
;
45 using ::com::sun::star::uno::Reference
;
46 using ::com::sun::star::uno::UNO_QUERY
;
47 using ::com::sun::star::uno::UNO_QUERY_THROW
;
49 ScDPCache::GroupItems::GroupItems() : mnGroupType(0) {}
51 ScDPCache::GroupItems::GroupItems(const ScDPNumGroupInfo
& rInfo
, sal_Int32 nGroupType
) :
52 maInfo(rInfo
), mnGroupType(nGroupType
) {}
54 ScDPCache::Field::Field() : mnNumFormat(0) {}
56 ScDPCache::ScDPCache(ScDocument
* pDoc
) :
59 maEmptyRows(0, MAXROW
, true),
68 struct ClearObjectSource
: std::unary_function
<ScDPObject
*, void>
70 void operator() (ScDPObject
* p
) const
78 ScDPCache::~ScDPCache()
80 // Make sure no live ScDPObject instances hold reference to this cache any
83 std::for_each(maRefObjects
.begin(), maRefObjects
.end(), ClearObjectSource());
89 * While the macro interpret level is incremented, the formula cells are
90 * (semi-)guaranteed to be interpreted.
92 class MacroInterpretIncrementer
95 MacroInterpretIncrementer(ScDocument
* pDoc
) :
98 mpDoc
->IncMacroInterpretLevel();
100 ~MacroInterpretIncrementer()
102 mpDoc
->DecMacroInterpretLevel();
108 OUString
createLabelString(ScDocument
* pDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
110 OUString aDocStr
= pDoc
->GetString(nCol
, nRow
, nTab
);
111 if (aDocStr
.isEmpty())
113 // Replace an empty label string with column name.
115 aBuf
.append(ScGlobal::GetRscString(STR_COLUMN
));
118 ScAddress
aColAddr(nCol
, 0, 0);
119 aBuf
.append(aColAddr
.Format(SCA_VALID_COL
, NULL
));
120 aDocStr
= aBuf
.makeStringAndClear();
126 ScDPCache
& rCache
, ScDocument
* pDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
,
127 ScDPItemData
& rData
, sal_uLong
& rNumFormat
)
129 OUString aDocStr
= pDoc
->GetString(nCol
, nRow
, nTab
);
132 ScAddress
aPos(nCol
, nRow
, nTab
);
134 if (pDoc
->GetErrCode(aPos
))
136 rData
.SetErrorString(rCache
.InternString(aDocStr
));
138 else if (pDoc
->HasValueData(nCol
, nRow
, nTab
))
140 double fVal
= pDoc
->GetValue(aPos
);
141 rNumFormat
= pDoc
->GetNumberFormat(aPos
);
142 rData
.SetValue(fVal
);
144 else if (pDoc
->HasData(nCol
, nRow
, nTab
))
146 rData
.SetString(rCache
.InternString(aDocStr
));
154 ScDPItemData maValue
;
157 SCROW mnValueSortIndex
;
158 Bucket(const ScDPItemData
& rValue
, SCROW nOrder
, SCROW nData
) :
159 maValue(rValue
), mnOrderIndex(nOrder
), mnDataIndex(nData
), mnValueSortIndex(0) {}
162 #if DEBUG_PIVOT_TABLE
167 struct PrintBucket
: std::unary_function
<Bucket
, void>
169 void operator() (const Bucket
& v
) const
171 cout
<< "value: " << v
.maValue
.GetValue() << " order index: " << v
.mnOrderIndex
<< " data index: " << v
.mnDataIndex
<< " value sort index: " << v
.mnValueSortIndex
<< endl
;
177 struct LessByValue
: std::binary_function
<Bucket
, Bucket
, bool>
179 bool operator() (const Bucket
& left
, const Bucket
& right
) const
181 return left
.maValue
< right
.maValue
;
185 struct LessByValueSortIndex
: std::binary_function
<Bucket
, Bucket
, bool>
187 bool operator() (const Bucket
& left
, const Bucket
& right
) const
189 return left
.mnValueSortIndex
< right
.mnValueSortIndex
;
193 struct LessByDataIndex
: std::binary_function
<Bucket
, Bucket
, bool>
195 bool operator() (const Bucket
& left
, const Bucket
& right
) const
197 return left
.mnDataIndex
< right
.mnDataIndex
;
201 struct EqualByOrderIndex
: std::binary_function
<Bucket
, Bucket
, bool>
203 bool operator() (const Bucket
& left
, const Bucket
& right
) const
205 return left
.mnOrderIndex
== right
.mnOrderIndex
;
209 class PushBackValue
: std::unary_function
<Bucket
, void>
211 ScDPCache::ItemsType
& mrItems
;
213 PushBackValue(ScDPCache::ItemsType
& _items
) : mrItems(_items
) {}
214 void operator() (const Bucket
& v
)
216 mrItems
.push_back(v
.maValue
);
220 class PushBackOrderIndex
: std::unary_function
<Bucket
, void>
222 ScDPCache::IndexArrayType
& mrData
;
224 PushBackOrderIndex(ScDPCache::IndexArrayType
& _items
) : mrData(_items
) {}
225 void operator() (const Bucket
& v
)
227 mrData
.push_back(v
.mnOrderIndex
);
231 class TagValueSortOrder
: std::unary_function
<Bucket
, void>
235 TagValueSortOrder() : mnCurIndex(0) {}
236 void operator() (Bucket
& v
)
238 v
.mnValueSortIndex
= mnCurIndex
++;
242 void processBuckets(std::vector
<Bucket
>& aBuckets
, ScDPCache::Field
& rField
)
244 if (aBuckets
.empty())
247 // Sort by the value.
248 std::sort(aBuckets
.begin(), aBuckets
.end(), LessByValue());
250 // Remember this sort order.
251 std::for_each(aBuckets
.begin(), aBuckets
.end(), TagValueSortOrder());
254 // Set order index such that unique values have identical index value.
256 std::vector
<Bucket
>::iterator it
= aBuckets
.begin(), itEnd
= aBuckets
.end();
257 ScDPItemData aPrev
= it
->maValue
;
258 it
->mnOrderIndex
= nCurIndex
;
259 for (++it
; it
!= itEnd
; ++it
)
261 if (!aPrev
.IsCaseInsEqual(it
->maValue
))
264 it
->mnOrderIndex
= nCurIndex
;
269 // Re-sort the bucket this time by the data index.
270 std::sort(aBuckets
.begin(), aBuckets
.end(), LessByDataIndex());
272 // Copy the order index series into the field object.
273 rField
.maData
.reserve(aBuckets
.size());
274 std::for_each(aBuckets
.begin(), aBuckets
.end(), PushBackOrderIndex(rField
.maData
));
276 // Sort by the value again.
277 std::sort(aBuckets
.begin(), aBuckets
.end(), LessByValueSortIndex());
280 std::vector
<Bucket
>::iterator itUniqueEnd
=
281 std::unique(aBuckets
.begin(), aBuckets
.end(), EqualByOrderIndex());
283 // Copy the unique values into items.
284 std::vector
<Bucket
>::iterator itBeg
= aBuckets
.begin();
285 size_t nLen
= distance(itBeg
, itUniqueEnd
);
286 rField
.maItems
.reserve(nLen
);
287 std::for_each(itBeg
, itUniqueEnd
, PushBackValue(rField
.maItems
));
292 bool ScDPCache::InitFromDoc(ScDocument
* pDoc
, const ScRange
& rRange
)
296 // Make sure the formula cells within the data range are interpreted
297 // during this call, for this method may be called from the interpretation
298 // of GETPIVOTDATA, which disables nested formula interpretation without
299 // increasing the macro level.
300 MacroInterpretIncrementer
aMacroInc(pDoc
);
302 SCROW nStartRow
= rRange
.aStart
.Row(); // start of data
303 SCROW nEndRow
= rRange
.aEnd
.Row();
306 if (!ValidRow(nStartRow
) || !ValidRow(nEndRow
) || nEndRow
<= nStartRow
)
309 sal_uInt16 nStartCol
= rRange
.aStart
.Col();
310 sal_uInt16 nEndCol
= rRange
.aEnd
.Col();
311 sal_uInt16 nDocTab
= rRange
.aStart
.Tab();
313 mnColumnCount
= nEndCol
- nStartCol
+ 1;
315 // this row count must include the trailing empty rows.
316 mnRowCount
= nEndRow
- nStartRow
; // skip the topmost label row.
318 // Skip trailing empty rows if exists.
319 SCCOL nCol1
= nStartCol
, nCol2
= nEndCol
;
320 SCROW nRow1
= nStartRow
, nRow2
= nEndRow
;
321 pDoc
->ShrinkToDataArea(nDocTab
, nCol1
, nRow1
, nCol2
, nRow2
);
322 bool bTailEmptyRows
= nEndRow
> nRow2
; // Trailing empty rows exist.
325 if (nEndRow
<= nStartRow
)
327 // Check this again since the end row position has changed. It's
328 // possible that the new end row becomes lower than the start row
329 // after the shrinkage.
334 maFields
.reserve(mnColumnCount
);
335 for (size_t i
= 0; i
< static_cast<size_t>(mnColumnCount
); ++i
)
336 maFields
.push_back(new Field
);
338 maLabelNames
.reserve(mnColumnCount
+1);
341 for (sal_uInt16 nCol
= nStartCol
; nCol
<= nEndCol
; ++nCol
)
343 AddLabel(createLabelString(pDoc
, nCol
, nStartRow
, nDocTab
));
344 Field
& rField
= maFields
[nCol
-nStartCol
];
345 std::vector
<Bucket
> aBuckets
;
346 aBuckets
.reserve(nEndRow
-nStartRow
); // skip the topmost label cell.
348 // Push back all original values.
349 SCROW nOffset
= nStartRow
+ 1;
350 for (SCROW i
= 0, n
= nEndRow
-nStartRow
; i
< n
; ++i
)
352 SCROW nRow
= i
+ nOffset
;
353 sal_uLong nNumFormat
= 0;
354 initFromCell(*this, pDoc
, nCol
, nRow
, nDocTab
, aData
, nNumFormat
);
355 aBuckets
.push_back(Bucket(aData
, 0, i
));
357 if (!aData
.IsEmpty())
359 maEmptyRows
.insert_back(i
, i
+1, false);
361 // Only take non-default number format.
362 rField
.mnNumFormat
= nNumFormat
;
366 processBuckets(aBuckets
, rField
);
370 // If the last item is not empty, append one. Note that the items
371 // are sorted, and empty item should come last when sorted.
372 if (rField
.maItems
.empty() || !rField
.maItems
.back().IsEmpty())
375 rField
.maItems
.push_back(aData
);
384 bool ScDPCache::InitFromDataBase(DBConnector
& rDB
)
390 mnColumnCount
= rDB
.getColumnCount();
392 maFields
.reserve(mnColumnCount
);
393 for (size_t i
= 0; i
< static_cast<size_t>(mnColumnCount
); ++i
)
394 maFields
.push_back(new Field
);
396 // Get column titles and types.
397 maLabelNames
.clear();
398 maLabelNames
.reserve(mnColumnCount
+1);
400 for (sal_Int32 nCol
= 0; nCol
< mnColumnCount
; ++nCol
)
402 OUString aColTitle
= rDB
.getColumnLabel(nCol
);
406 std::vector
<Bucket
> aBuckets
;
408 for (sal_Int32 nCol
= 0; nCol
< mnColumnCount
; ++nCol
)
414 Field
& rField
= maFields
[nCol
];
418 short nFormatType
= css::util::NumberFormat::UNDEFINED
;
420 rDB
.getValue(nCol
, aData
, nFormatType
);
421 aBuckets
.push_back(Bucket(aData
, 0, nRow
));
422 if (!aData
.IsEmpty())
424 maEmptyRows
.insert_back(nRow
, nRow
+1, false);
425 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
426 rField
.mnNumFormat
= pFormatter
? pFormatter
->GetStandardFormat(nFormatType
) : 0;
433 processBuckets(aBuckets
, rField
);
438 if (!maFields
.empty())
439 mnRowCount
= maFields
[0].maData
.size();
444 catch (const Exception
&)
450 bool ScDPCache::ValidQuery( SCROW nRow
, const ScQueryParam
&rParam
) const
452 if (!rParam
.GetEntryCount())
455 if (!rParam
.GetEntry(0).bDoQuery
)
458 bool bMatchWholeCell
= mpDoc
->GetDocOptions().IsMatchWholeCell();
460 SCSIZE nEntryCount
= rParam
.GetEntryCount();
461 std::vector
<bool> aPassed(nEntryCount
, false);
464 CollatorWrapper
* pCollator
= (rParam
.bCaseSens
? ScGlobal::GetCaseCollator() :
465 ScGlobal::GetCollator() );
466 ::utl::TransliterationWrapper
* pTransliteration
= (rParam
.bCaseSens
?
467 ScGlobal::GetCaseTransliteration() : ScGlobal::GetpTransliteration());
469 for (size_t i
= 0; i
< nEntryCount
&& rParam
.GetEntry(i
).bDoQuery
; ++i
)
471 const ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
472 const ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
473 // we can only handle one single direct query
474 // #i115431# nField in QueryParam is the sheet column, not the field within the source range
475 SCCOL nQueryCol
= (SCCOL
)rEntry
.nField
;
476 if ( nQueryCol
< rParam
.nCol1
)
477 nQueryCol
= rParam
.nCol1
;
478 if ( nQueryCol
> rParam
.nCol2
)
479 nQueryCol
= rParam
.nCol2
;
480 SCCOL nSourceField
= nQueryCol
- rParam
.nCol1
;
481 SCROW nId
= GetItemDataId( nSourceField
, nRow
, false );
482 const ScDPItemData
* pCellData
= GetItemDataById( nSourceField
, nId
);
486 if (rEntry
.GetQueryItem().meType
== ScQueryEntry::ByEmpty
)
488 if (rEntry
.IsQueryByEmpty())
489 bOk
= pCellData
->IsEmpty();
492 OSL_ASSERT(rEntry
.IsQueryByNonEmpty());
493 bOk
= !pCellData
->IsEmpty();
496 else if (rEntry
.GetQueryItem().meType
!= ScQueryEntry::ByString
&& pCellData
->IsValue())
498 double nCellVal
= pCellData
->GetValue();
503 bOk
= ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
506 bOk
= (nCellVal
< rItem
.mfVal
) && !::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
509 bOk
= (nCellVal
> rItem
.mfVal
) && !::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
512 bOk
= (nCellVal
< rItem
.mfVal
) || ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
514 case SC_GREATER_EQUAL
:
515 bOk
= (nCellVal
> rItem
.mfVal
) || ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
518 bOk
= !::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
525 else if ((rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
)
526 || (rEntry
.GetQueryItem().meType
== ScQueryEntry::ByString
527 && pCellData
->HasStringData() )
530 OUString aCellStr
= pCellData
->GetString();
532 bool bRealRegExp
= (rParam
.bRegExp
&& ((rEntry
.eOp
== SC_EQUAL
)
533 || (rEntry
.eOp
== SC_NOT_EQUAL
)));
534 bool bTestRegExp
= false;
535 if (bRealRegExp
|| bTestRegExp
)
537 sal_Int32 nStart
= 0;
538 sal_Int32 nEnd
= aCellStr
.getLength();
540 bool bMatch
= (bool) rEntry
.GetSearchTextPtr( rParam
.bCaseSens
)
541 ->SearchForward( aCellStr
, &nStart
, &nEnd
);
542 // from 614 on, nEnd is behind the found text
543 if (bMatch
&& bMatchWholeCell
544 && (nStart
!= 0 || nEnd
!= aCellStr
.getLength()))
545 bMatch
= false; // RegExp must match entire cell string
547 bOk
= ((rEntry
.eOp
== SC_NOT_EQUAL
) ? !bMatch
: bMatch
);
551 if (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
)
555 // TODO: Use shared string for fast equality check.
556 OUString aStr
= rEntry
.GetQueryItem().maString
.getString();
557 bOk
= pTransliteration
->isEqual(aCellStr
, aStr
);
558 bool bHasStar
= false;
560 if (( nIndex
= aStr
.indexOf('*') ) != -1)
562 if (bHasStar
&& (nIndex
>0))
564 for (sal_Int32 j
=0;(j
<nIndex
) && (j
< aCellStr
.getLength()) ; j
++)
566 if (aCellStr
[j
] == aStr
[j
])
580 OUString aQueryStr
= rEntry
.GetQueryItem().maString
.getString();
581 ::com::sun::star::uno::Sequence
< sal_Int32
> xOff
;
582 OUString aCell
= pTransliteration
->transliterate(
583 aCellStr
, ScGlobal::eLnge
, 0, aCellStr
.getLength(), &xOff
);
584 OUString aQuer
= pTransliteration
->transliterate(
585 aQueryStr
, ScGlobal::eLnge
, 0, aQueryStr
.getLength(), &xOff
);
586 bOk
= (aCell
.indexOf( aQuer
) != -1);
588 if (rEntry
.eOp
== SC_NOT_EQUAL
)
592 { // use collator here because data was probably sorted
593 sal_Int32 nCompare
= pCollator
->compareString(
594 aCellStr
, rEntry
.GetQueryItem().maString
.getString());
598 bOk
= (nCompare
< 0);
601 bOk
= (nCompare
> 0);
604 bOk
= (nCompare
<= 0);
606 case SC_GREATER_EQUAL
:
607 bOk
= (nCompare
>= 0);
610 OSL_FAIL("SC_NOT_EQUAL");
630 if (rEntry
.eConnect
== SC_AND
)
632 aPassed
[nPos
] = aPassed
[nPos
] && bOk
;
642 for (long j
=1; j
<= nPos
; j
++)
643 aPassed
[0] = aPassed
[0] || aPassed
[j
];
645 bool bRet
= aPassed
[0];
649 ScDocument
* ScDPCache::GetDoc() const
654 long ScDPCache::GetColumnCount() const
656 return mnColumnCount
;
659 bool ScDPCache::IsRowEmpty(SCROW nRow
) const
662 maEmptyRows
.search_tree(nRow
, bEmpty
);
666 const ScDPCache::GroupItems
* ScDPCache::GetGroupItems(long nDim
) const
671 long nSourceCount
= static_cast<long>(maFields
.size());
672 if (nDim
< nSourceCount
)
673 return maFields
[nDim
].mpGroup
.get();
675 nDim
-= nSourceCount
;
676 if (nDim
< static_cast<long>(maGroupFields
.size()))
677 return &maGroupFields
[nDim
];
682 OUString
ScDPCache::GetDimensionName(LabelsType::size_type nDim
) const
684 OSL_ENSURE(nDim
< maLabelNames
.size()-1 , "ScDPTableDataCache::GetDimensionName");
685 OSL_ENSURE(maLabelNames
.size() == static_cast <sal_uInt16
> (mnColumnCount
+1), "ScDPTableDataCache::GetDimensionName");
687 if ( nDim
+1 < maLabelNames
.size() )
689 return maLabelNames
[nDim
+1];
697 typedef std::unordered_set
<OUString
, OUStringHash
> LabelSet
;
699 class InsertLabel
: public std::unary_function
<OUString
, void>
703 InsertLabel(LabelSet
& rNames
) : mrNames(rNames
) {}
704 void operator() (const OUString
& r
)
712 void ScDPCache::PostInit()
714 OSL_ENSURE(!maFields
.empty(), "Cache not initialized!");
716 maEmptyRows
.build_tree();
717 typedef mdds::flat_segment_tree
<SCROW
, bool>::const_reverse_iterator itr_type
;
718 itr_type it
= maEmptyRows
.rbegin();
719 OSL_ENSURE(it
!= maEmptyRows
.rend(), "corrupt flat_segment_tree instance!");
720 mnDataSize
= maFields
[0].maData
.size();
721 ++it
; // Skip the first position.
722 OSL_ENSURE(it
!= maEmptyRows
.rend(), "buggy version of flat_segment_tree is used.");
725 SCROW nLastNonEmpty
= it
->first
- 1;
726 if (nLastNonEmpty
+1 < mnDataSize
)
727 mnDataSize
= nLastNonEmpty
+1;
731 void ScDPCache::Clear()
736 maLabelNames
.clear();
737 maGroupFields
.clear();
739 maStringPool
.clear();
742 void ScDPCache::AddLabel(const OUString
& rLabel
)
745 if ( maLabelNames
.empty() )
746 maLabelNames
.push_back(ScGlobal::GetRscString(STR_PIVOT_DATA
));
748 //reset name if needed
749 LabelSet aExistingNames
;
750 std::for_each(maLabelNames
.begin(), maLabelNames
.end(), InsertLabel(aExistingNames
));
751 sal_Int32 nSuffix
= 1;
752 OUString aNewName
= rLabel
;
755 if (!aExistingNames
.count(aNewName
))
757 // unique name found!
758 maLabelNames
.push_back(aNewName
);
762 // Name already exists.
763 OUStringBuffer
aBuf(rLabel
);
764 aBuf
.append(++nSuffix
);
765 aNewName
= aBuf
.makeStringAndClear();
769 SCROW
ScDPCache::GetItemDataId(sal_uInt16 nDim
, SCROW nRow
, bool bRepeatIfEmpty
) const
771 OSL_ENSURE(nDim
< mnColumnCount
, "ScDPTableDataCache::GetItemDataId ");
773 const Field
& rField
= maFields
[nDim
];
774 if (static_cast<size_t>(nRow
) >= rField
.maData
.size())
776 // nRow is in the trailing empty rows area.
778 nRow
= rField
.maData
.size()-1; // Move to the last non-empty row.
780 // Return the last item, which should always be empty if the
781 // initialization has skipped trailing empty rows.
782 return rField
.maItems
.size()-1;
785 else if (bRepeatIfEmpty
)
787 while (nRow
> 0 && rField
.maItems
[rField
.maData
[nRow
]].IsEmpty())
791 return rField
.maData
[nRow
];
794 const ScDPItemData
* ScDPCache::GetItemDataById(long nDim
, SCROW nId
) const
796 if (nDim
< 0 || nId
< 0)
799 size_t nSourceCount
= maFields
.size();
800 size_t nDimPos
= static_cast<size_t>(nDim
);
801 size_t nItemId
= static_cast<size_t>(nId
);
802 if (nDimPos
< nSourceCount
)
805 const Field
& rField
= maFields
[nDimPos
];
806 if (nItemId
< rField
.maItems
.size())
807 return &rField
.maItems
[nItemId
];
812 nItemId
-= rField
.maItems
.size();
813 const ItemsType
& rGI
= rField
.mpGroup
->maItems
;
814 if (nItemId
>= rGI
.size())
817 return &rGI
[nItemId
];
821 nDimPos
-= nSourceCount
;
822 if (nDimPos
>= maGroupFields
.size())
825 const ItemsType
& rGI
= maGroupFields
[nDimPos
].maItems
;
826 if (nItemId
>= rGI
.size())
829 return &rGI
[nItemId
];
832 size_t ScDPCache::GetFieldCount() const
834 return maFields
.size();
837 size_t ScDPCache::GetGroupFieldCount() const
839 return maGroupFields
.size();
842 SCROW
ScDPCache::GetRowCount() const
847 SCROW
ScDPCache::GetDataSize() const
849 OSL_ENSURE(mnDataSize
<= GetRowCount(), "Data size should never be larger than the row count.");
850 return mnDataSize
>= 0 ? mnDataSize
: 0;
853 const ScDPCache::IndexArrayType
* ScDPCache::GetFieldIndexArray( size_t nDim
) const
855 if (nDim
>= maFields
.size())
858 return &maFields
[nDim
].maData
;
861 const ScDPCache::ItemsType
& ScDPCache::GetDimMemberValues(SCCOL nDim
) const
863 OSL_ENSURE( nDim
>=0 && nDim
< mnColumnCount
," nDim < mnColumnCount ");
864 return maFields
.at(nDim
).maItems
;
867 sal_uLong
ScDPCache::GetNumberFormat( long nDim
) const
869 if ( nDim
>= mnColumnCount
)
872 // TODO: Find a way to determine the dominant number format in presence of
873 // multiple number formats in the same field.
874 return maFields
[nDim
].mnNumFormat
;
877 bool ScDPCache::IsDateDimension( long nDim
) const
879 if (nDim
>= mnColumnCount
)
882 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
886 short eType
= pFormatter
->GetType(maFields
[nDim
].mnNumFormat
);
887 return (eType
== css::util::NumberFormat::DATE
) || (eType
== css::util::NumberFormat::DATETIME
);
890 long ScDPCache::GetDimMemberCount(long nDim
) const
892 OSL_ENSURE( nDim
>=0 && nDim
< mnColumnCount
," ScDPTableDataCache::GetDimMemberCount : out of bound ");
893 return maFields
[nDim
].maItems
.size();
896 SCCOL
ScDPCache::GetDimensionIndex(const OUString
& sName
) const
898 for (size_t i
= 1; i
< maLabelNames
.size(); ++i
)
900 if (maLabelNames
[i
].equals(sName
))
901 return static_cast<SCCOL
>(i
-1);
906 const OUString
* ScDPCache::InternString(const OUString
& rStr
) const
908 StringSetType::iterator it
= maStringPool
.find(rStr
);
909 if (it
!= maStringPool
.end())
913 std::pair
<StringSetType::iterator
, bool> r
= maStringPool
.insert(rStr
);
914 return r
.second
? &(*r
.first
) : NULL
;
917 void ScDPCache::AddReference(ScDPObject
* pObj
) const
919 maRefObjects
.insert(pObj
);
922 void ScDPCache::RemoveReference(ScDPObject
* pObj
) const
925 // Object being deleted.
928 maRefObjects
.erase(pObj
);
929 if (maRefObjects
.empty())
930 mpDoc
->GetDPCollection()->RemoveCache(this);
933 const ScDPCache::ObjectSetType
& ScDPCache::GetAllReferences() const
938 SCROW
ScDPCache::GetIdByItemData(long nDim
, const ScDPItemData
& rItem
) const
943 if (nDim
< mnColumnCount
)
946 const ItemsType
& rItems
= maFields
[nDim
].maItems
;
947 for (size_t i
= 0, n
= rItems
.size(); i
< n
; ++i
)
949 if (rItems
[i
] == rItem
)
953 if (!maFields
[nDim
].mpGroup
)
956 // grouped source field.
957 const ItemsType
& rGI
= maFields
[nDim
].mpGroup
->maItems
;
958 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
961 return rItems
.size() + i
;
967 nDim
-= mnColumnCount
;
968 if (static_cast<size_t>(nDim
) < maGroupFields
.size())
970 const ItemsType
& rGI
= maGroupFields
[nDim
].maItems
;
971 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
981 OUString
ScDPCache::GetFormattedString(long nDim
, const ScDPItemData
& rItem
) const
984 return rItem
.GetString();
986 ScDPItemData::Type eType
= rItem
.GetType();
987 if (eType
== ScDPItemData::Value
)
989 // Format value using the stored number format.
990 sal_uLong nNumFormat
= GetNumberFormat(nDim
);
991 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
994 Color
* pColor
= NULL
;
996 pFormatter
->GetOutputString(rItem
.GetValue(), nNumFormat
, aStr
, &pColor
);
1001 if (eType
== ScDPItemData::GroupValue
)
1003 ScDPItemData::GroupValueAttr aAttr
= rItem
.GetGroupValue();
1004 double fStart
= 0.0, fEnd
= 0.0;
1005 const GroupItems
* p
= GetGroupItems(nDim
);
1008 fStart
= p
->maInfo
.mfStart
;
1009 fEnd
= p
->maInfo
.mfEnd
;
1011 return ScDPUtil::getDateGroupName(
1012 aAttr
.mnGroupType
, aAttr
.mnValue
, mpDoc
->GetFormatTable(), fStart
, fEnd
);
1015 if (eType
== ScDPItemData::RangeStart
)
1017 double fVal
= rItem
.GetValue();
1018 const GroupItems
* p
= GetGroupItems(nDim
);
1020 return rItem
.GetString();
1022 sal_Unicode cDecSep
= ScGlobal::pLocaleData
->getNumDecimalSep()[0];
1023 return ScDPUtil::getNumGroupName(fVal
, p
->maInfo
, cDecSep
, mpDoc
->GetFormatTable());
1026 return rItem
.GetString();
1029 long ScDPCache::AppendGroupField()
1031 maGroupFields
.push_back(new GroupItems
);
1032 return static_cast<long>(maFields
.size() + maGroupFields
.size() - 1);
1035 void ScDPCache::ResetGroupItems(long nDim
, const ScDPNumGroupInfo
& rNumInfo
, sal_Int32 nGroupType
)
1040 long nSourceCount
= static_cast<long>(maFields
.size());
1041 if (nDim
< nSourceCount
)
1043 maFields
.at(nDim
).mpGroup
.reset(new GroupItems(rNumInfo
, nGroupType
));
1047 nDim
-= nSourceCount
;
1048 if (nDim
< static_cast<long>(maGroupFields
.size()))
1050 GroupItems
& rGI
= maGroupFields
[nDim
];
1051 rGI
.maItems
.clear();
1052 rGI
.maInfo
= rNumInfo
;
1053 rGI
.mnGroupType
= nGroupType
;
1057 SCROW
ScDPCache::SetGroupItem(long nDim
, const ScDPItemData
& rData
)
1062 long nSourceCount
= static_cast<long>(maFields
.size());
1063 if (nDim
< nSourceCount
)
1065 GroupItems
& rGI
= *maFields
.at(nDim
).mpGroup
;
1066 rGI
.maItems
.push_back(rData
);
1067 SCROW nId
= maFields
[nDim
].maItems
.size() + rGI
.maItems
.size() - 1;
1071 nDim
-= nSourceCount
;
1072 if (nDim
< static_cast<long>(maGroupFields
.size()))
1074 ItemsType
& rItems
= maGroupFields
.at(nDim
).maItems
;
1075 rItems
.push_back(rData
);
1076 return rItems
.size()-1;
1082 void ScDPCache::GetGroupDimMemberIds(long nDim
, std::vector
<SCROW
>& rIds
) const
1087 long nSourceCount
= static_cast<long>(maFields
.size());
1088 if (nDim
< nSourceCount
)
1090 if (!maFields
.at(nDim
).mpGroup
)
1093 size_t nOffset
= maFields
[nDim
].maItems
.size();
1094 const ItemsType
& rGI
= maFields
[nDim
].mpGroup
->maItems
;
1095 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
1096 rIds
.push_back(static_cast<SCROW
>(i
+ nOffset
));
1101 nDim
-= nSourceCount
;
1102 if (nDim
< static_cast<long>(maGroupFields
.size()))
1104 const ItemsType
& rGI
= maGroupFields
.at(nDim
).maItems
;
1105 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
1106 rIds
.push_back(static_cast<SCROW
>(i
));
1112 struct ClearGroupItems
: std::unary_function
<ScDPCache::Field
, void>
1114 void operator() (ScDPCache::Field
& r
) const
1122 void ScDPCache::ClearGroupFields()
1124 maGroupFields
.clear();
1125 std::for_each(maFields
.begin(), maFields
.end(), ClearGroupItems());
1128 const ScDPNumGroupInfo
* ScDPCache::GetNumGroupInfo(long nDim
) const
1133 long nSourceCount
= static_cast<long>(maFields
.size());
1134 if (nDim
< nSourceCount
)
1136 if (!maFields
.at(nDim
).mpGroup
)
1139 return &maFields
[nDim
].mpGroup
->maInfo
;
1142 nDim
-= nSourceCount
;
1143 if (nDim
< static_cast<long>(maGroupFields
.size()))
1144 return &maGroupFields
.at(nDim
).maInfo
;
1149 sal_Int32
ScDPCache::GetGroupType(long nDim
) const
1154 long nSourceCount
= static_cast<long>(maFields
.size());
1155 if (nDim
< nSourceCount
)
1157 if (!maFields
.at(nDim
).mpGroup
)
1160 return maFields
[nDim
].mpGroup
->mnGroupType
;
1163 nDim
-= nSourceCount
;
1164 if (nDim
< static_cast<long>(maGroupFields
.size()))
1165 return maGroupFields
.at(nDim
).mnGroupType
;
1170 SCROW
ScDPCache::GetOrder(long /*nDim*/, SCROW nIndex
)
1176 #if DEBUG_PIVOT_TABLE
1180 std::ostream
& operator<< (::std::ostream
& os
, const OUString
& str
)
1182 return os
<< OUStringToOString(str
, RTL_TEXTENCODING_UTF8
).getStr();
1185 void dumpItems(const ScDPCache
& rCache
, long nDim
, const ScDPCache::ItemsType
& rItems
, size_t nOffset
)
1187 for (size_t i
= 0; i
< rItems
.size(); ++i
)
1188 cout
<< " " << (i
+nOffset
) << ": " << rCache
.GetFormattedString(nDim
, rItems
[i
]) << endl
;
1191 void dumpSourceData(const ScDPCache
& rCache
, long nDim
, const ScDPCache::ItemsType
& rItems
, const ScDPCache::IndexArrayType
& rArray
)
1193 ScDPCache::IndexArrayType::const_iterator it
= rArray
.begin(), itEnd
= rArray
.end();
1194 for (; it
!= itEnd
; ++it
)
1195 cout
<< " '" << rCache
.GetFormattedString(nDim
, rItems
[*it
]) << "'" << endl
;
1198 const char* getGroupTypeName(sal_Int32 nType
)
1200 static const char* pNames
[] = {
1201 "", "years", "quarters", "months", "days", "hours", "minutes", "seconds"
1206 case sheet::DataPilotFieldGroupBy::YEARS
: return pNames
[1];
1207 case sheet::DataPilotFieldGroupBy::QUARTERS
: return pNames
[2];
1208 case sheet::DataPilotFieldGroupBy::MONTHS
: return pNames
[3];
1209 case sheet::DataPilotFieldGroupBy::DAYS
: return pNames
[4];
1210 case sheet::DataPilotFieldGroupBy::HOURS
: return pNames
[5];
1211 case sheet::DataPilotFieldGroupBy::MINUTES
: return pNames
[6];
1212 case sheet::DataPilotFieldGroupBy::SECONDS
: return pNames
[7];
1222 void ScDPCache::Dump() const
1224 // Change these flags to fit your debugging needs.
1225 bool bDumpItems
= false;
1226 bool bDumpSourceData
= false;
1228 cout
<< "--- pivot cache dump" << endl
;
1230 FieldsType::const_iterator it
= maFields
.begin(), itEnd
= maFields
.end();
1231 for (size_t i
= 0; it
!= itEnd
; ++it
, ++i
)
1233 const Field
& fld
= *it
;
1234 cout
<< "* source dimension: " << GetDimensionName(i
) << " (ID = " << i
<< ")" << endl
;
1235 cout
<< " item count: " << fld
.maItems
.size() << endl
;
1237 dumpItems(*this, i
, fld
.maItems
, 0);
1240 cout
<< " group item count: " << fld
.mpGroup
->maItems
.size() << endl
;
1241 cout
<< " group type: " << getGroupTypeName(fld
.mpGroup
->mnGroupType
) << endl
;
1243 dumpItems(*this, i
, fld
.mpGroup
->maItems
, fld
.maItems
.size());
1246 if (bDumpSourceData
)
1248 cout
<< " source data (re-constructed):" << endl
;
1249 dumpSourceData(*this, i
, fld
.maItems
, fld
.maData
);
1255 GroupFieldsType::const_iterator it
= maGroupFields
.begin(), itEnd
= maGroupFields
.end();
1256 for (size_t i
= maFields
.size(); it
!= itEnd
; ++it
, ++i
)
1258 const GroupItems
& gi
= *it
;
1259 cout
<< "* group dimension: (unnamed) (ID = " << i
<< ")" << endl
;
1260 cout
<< " item count: " << gi
.maItems
.size() << endl
;
1261 cout
<< " group type: " << getGroupTypeName(gi
.mnGroupType
) << endl
;
1263 dumpItems(*this, i
, gi
.maItems
, 0);
1268 struct { SCROW start
; SCROW end
; bool empty
; } aRange
;
1269 cout
<< "* empty rows: " << endl
;
1270 mdds::flat_segment_tree
<SCROW
, bool>::const_iterator it
= maEmptyRows
.begin(), itEnd
= maEmptyRows
.end();
1273 aRange
.start
= it
->first
;
1274 aRange
.empty
= it
->second
;
1278 for (; it
!= itEnd
; ++it
)
1280 aRange
.end
= it
->first
-1;
1281 cout
<< " rows " << aRange
.start
<< "-" << aRange
.end
<< ": " << (aRange
.empty
? "empty" : "not-empty") << endl
;
1282 aRange
.start
= it
->first
;
1283 aRange
.empty
= it
->second
;
1287 cout
<< "---" << endl
;
1292 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */