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>
41 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
44 using namespace ::com::sun::star
;
46 using ::com::sun::star::uno::Exception
;
47 using ::com::sun::star::uno::Reference
;
48 using ::com::sun::star::uno::UNO_QUERY
;
49 using ::com::sun::star::uno::UNO_QUERY_THROW
;
51 ScDPCache::GroupItems::GroupItems() : mnGroupType(0) {}
53 ScDPCache::GroupItems::GroupItems(const ScDPNumGroupInfo
& rInfo
, sal_Int32 nGroupType
) :
54 maInfo(rInfo
), mnGroupType(nGroupType
) {}
56 ScDPCache::Field::Field() : mnNumFormat(0) {}
58 ScDPCache::ScDPCache(ScDocument
* pDoc
) :
61 maEmptyRows(0, MAXROW
, true),
70 struct ClearObjectSource
: std::unary_function
<ScDPObject
*, void>
72 void operator() (ScDPObject
* p
) const
80 ScDPCache::~ScDPCache()
82 // Make sure no live ScDPObject instances hold reference to this cache any
85 std::for_each(maRefObjects
.begin(), maRefObjects
.end(), ClearObjectSource());
91 * While the macro interpret level is incremented, the formula cells are
92 * (semi-)guaranteed to be interpreted.
94 class MacroInterpretIncrementer
97 MacroInterpretIncrementer(ScDocument
* pDoc
) :
100 mpDoc
->IncMacroInterpretLevel();
102 ~MacroInterpretIncrementer()
104 mpDoc
->DecMacroInterpretLevel();
110 OUString
createLabelString(ScDocument
* pDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
112 OUString aDocStr
= pDoc
->GetString(nCol
, nRow
, nTab
);
113 if (aDocStr
.isEmpty())
115 // Replace an empty label string with column name.
117 aBuf
.append(ScGlobal::GetRscString(STR_COLUMN
));
120 ScAddress
aColAddr(nCol
, 0, 0);
121 aBuf
.append(aColAddr
.Format(SCA_VALID_COL
, NULL
));
122 aDocStr
= aBuf
.makeStringAndClear();
128 ScDPCache
& rCache
, ScDocument
* pDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
,
129 ScDPItemData
& rData
, sal_uLong
& rNumFormat
)
131 OUString aDocStr
= pDoc
->GetString(nCol
, nRow
, nTab
);
134 ScAddress
aPos(nCol
, nRow
, nTab
);
136 if (pDoc
->GetErrCode(aPos
))
138 rData
.SetErrorString(rCache
.InternString(aDocStr
));
140 else if (pDoc
->HasValueData(nCol
, nRow
, nTab
))
142 double fVal
= pDoc
->GetValue(aPos
);
143 rNumFormat
= pDoc
->GetNumberFormat(aPos
);
144 rData
.SetValue(fVal
);
146 else if (pDoc
->HasData(nCol
, nRow
, nTab
))
148 rData
.SetString(rCache
.InternString(aDocStr
));
156 ScDPItemData maValue
;
159 SCROW mnValueSortIndex
;
160 Bucket(const ScDPItemData
& rValue
, SCROW nOrder
, SCROW nData
) :
161 maValue(rValue
), mnOrderIndex(nOrder
), mnDataIndex(nData
), mnValueSortIndex(0) {}
164 #if DEBUG_PIVOT_TABLE
169 struct PrintBucket
: std::unary_function
<Bucket
, void>
171 void operator() (const Bucket
& v
) const
173 cout
<< "value: " << v
.maValue
.GetValue() << " order index: " << v
.mnOrderIndex
<< " data index: " << v
.mnDataIndex
<< " value sort index: " << v
.mnValueSortIndex
<< endl
;
179 struct LessByValue
: std::binary_function
<Bucket
, Bucket
, bool>
181 bool operator() (const Bucket
& left
, const Bucket
& right
) const
183 return left
.maValue
< right
.maValue
;
187 struct LessByValueSortIndex
: std::binary_function
<Bucket
, Bucket
, bool>
189 bool operator() (const Bucket
& left
, const Bucket
& right
) const
191 return left
.mnValueSortIndex
< right
.mnValueSortIndex
;
195 struct LessByDataIndex
: std::binary_function
<Bucket
, Bucket
, bool>
197 bool operator() (const Bucket
& left
, const Bucket
& right
) const
199 return left
.mnDataIndex
< right
.mnDataIndex
;
203 struct EqualByOrderIndex
: std::binary_function
<Bucket
, Bucket
, bool>
205 bool operator() (const Bucket
& left
, const Bucket
& right
) const
207 return left
.mnOrderIndex
== right
.mnOrderIndex
;
211 class PushBackValue
: std::unary_function
<Bucket
, void>
213 ScDPCache::ItemsType
& mrItems
;
215 PushBackValue(ScDPCache::ItemsType
& _items
) : mrItems(_items
) {}
216 void operator() (const Bucket
& v
)
218 mrItems
.push_back(v
.maValue
);
222 class PushBackOrderIndex
: std::unary_function
<Bucket
, void>
224 ScDPCache::IndexArrayType
& mrData
;
226 PushBackOrderIndex(ScDPCache::IndexArrayType
& _items
) : mrData(_items
) {}
227 void operator() (const Bucket
& v
)
229 mrData
.push_back(v
.mnOrderIndex
);
233 class TagValueSortOrder
: std::unary_function
<Bucket
, void>
237 TagValueSortOrder() : mnCurIndex(0) {}
238 void operator() (Bucket
& v
)
240 v
.mnValueSortIndex
= mnCurIndex
++;
244 void processBuckets(std::vector
<Bucket
>& aBuckets
, ScDPCache::Field
& rField
)
246 if (aBuckets
.empty())
249 // Sort by the value.
250 std::sort(aBuckets
.begin(), aBuckets
.end(), LessByValue());
252 // Remember this sort order.
253 std::for_each(aBuckets
.begin(), aBuckets
.end(), TagValueSortOrder());
256 // Set order index such that unique values have identical index value.
258 std::vector
<Bucket
>::iterator it
= aBuckets
.begin(), itEnd
= aBuckets
.end();
259 ScDPItemData aPrev
= it
->maValue
;
260 it
->mnOrderIndex
= nCurIndex
;
261 for (++it
; it
!= itEnd
; ++it
)
263 if (!aPrev
.IsCaseInsEqual(it
->maValue
))
266 it
->mnOrderIndex
= nCurIndex
;
271 // Re-sort the bucket this time by the data index.
272 std::sort(aBuckets
.begin(), aBuckets
.end(), LessByDataIndex());
274 // Copy the order index series into the field object.
275 rField
.maData
.reserve(aBuckets
.size());
276 std::for_each(aBuckets
.begin(), aBuckets
.end(), PushBackOrderIndex(rField
.maData
));
278 // Sort by the value again.
279 std::sort(aBuckets
.begin(), aBuckets
.end(), LessByValueSortIndex());
282 std::vector
<Bucket
>::iterator itUniqueEnd
=
283 std::unique(aBuckets
.begin(), aBuckets
.end(), EqualByOrderIndex());
285 // Copy the unique values into items.
286 std::vector
<Bucket
>::iterator itBeg
= aBuckets
.begin();
287 size_t nLen
= distance(itBeg
, itUniqueEnd
);
288 rField
.maItems
.reserve(nLen
);
289 std::for_each(itBeg
, itUniqueEnd
, PushBackValue(rField
.maItems
));
294 bool ScDPCache::InitFromDoc(ScDocument
* pDoc
, const ScRange
& rRange
)
298 // Make sure the formula cells within the data range are interpreted
299 // during this call, for this method may be called from the interpretation
300 // of GETPIVOTDATA, which disables nested formula interpretation without
301 // increasing the macro level.
302 MacroInterpretIncrementer
aMacroInc(pDoc
);
304 SCROW nStartRow
= rRange
.aStart
.Row(); // start of data
305 SCROW nEndRow
= rRange
.aEnd
.Row();
308 if (!ValidRow(nStartRow
) || !ValidRow(nEndRow
) || nEndRow
<= nStartRow
)
311 sal_uInt16 nStartCol
= rRange
.aStart
.Col();
312 sal_uInt16 nEndCol
= rRange
.aEnd
.Col();
313 sal_uInt16 nDocTab
= rRange
.aStart
.Tab();
315 mnColumnCount
= nEndCol
- nStartCol
+ 1;
317 // this row count must include the trailing empty rows.
318 mnRowCount
= nEndRow
- nStartRow
; // skip the topmost label row.
320 // Skip trailing empty rows if exists.
321 SCCOL nCol1
= nStartCol
, nCol2
= nEndCol
;
322 SCROW nRow1
= nStartRow
, nRow2
= nEndRow
;
323 pDoc
->ShrinkToDataArea(nDocTab
, nCol1
, nRow1
, nCol2
, nRow2
);
324 bool bTailEmptyRows
= nEndRow
> nRow2
; // Trailing empty rows exist.
327 if (nEndRow
<= nStartRow
)
329 // Check this again since the end row position has changed. It's
330 // possible that the new end row becomes lower than the start row
331 // after the shrinkage.
336 maFields
.reserve(mnColumnCount
);
337 for (size_t i
= 0; i
< static_cast<size_t>(mnColumnCount
); ++i
)
338 maFields
.push_back(new Field
);
340 maLabelNames
.reserve(mnColumnCount
+1);
343 for (sal_uInt16 nCol
= nStartCol
; nCol
<= nEndCol
; ++nCol
)
345 AddLabel(createLabelString(pDoc
, nCol
, nStartRow
, nDocTab
));
346 Field
& rField
= maFields
[nCol
-nStartCol
];
347 std::vector
<Bucket
> aBuckets
;
348 aBuckets
.reserve(nEndRow
-nStartRow
); // skip the topmost label cell.
350 // Push back all original values.
351 SCROW nOffset
= nStartRow
+ 1;
352 for (SCROW i
= 0, n
= nEndRow
-nStartRow
; i
< n
; ++i
)
354 SCROW nRow
= i
+ nOffset
;
355 sal_uLong nNumFormat
= 0;
356 initFromCell(*this, pDoc
, nCol
, nRow
, nDocTab
, aData
, nNumFormat
);
357 aBuckets
.push_back(Bucket(aData
, 0, i
));
359 if (!aData
.IsEmpty())
361 maEmptyRows
.insert_back(i
, i
+1, false);
363 // Only take non-default number format.
364 rField
.mnNumFormat
= nNumFormat
;
368 processBuckets(aBuckets
, rField
);
372 // If the last item is not empty, append one. Note that the items
373 // are sorted, and empty item should come last when sorted.
374 if (rField
.maItems
.empty() || !rField
.maItems
.back().IsEmpty())
377 rField
.maItems
.push_back(aData
);
386 bool ScDPCache::InitFromDataBase(DBConnector
& rDB
)
392 mnColumnCount
= rDB
.getColumnCount();
394 maFields
.reserve(mnColumnCount
);
395 for (size_t i
= 0; i
< static_cast<size_t>(mnColumnCount
); ++i
)
396 maFields
.push_back(new Field
);
398 // Get column titles and types.
399 maLabelNames
.clear();
400 maLabelNames
.reserve(mnColumnCount
+1);
402 for (sal_Int32 nCol
= 0; nCol
< mnColumnCount
; ++nCol
)
404 OUString aColTitle
= rDB
.getColumnLabel(nCol
);
408 std::vector
<Bucket
> aBuckets
;
410 for (sal_Int32 nCol
= 0; nCol
< mnColumnCount
; ++nCol
)
416 Field
& rField
= maFields
[nCol
];
420 short nFormatType
= NUMBERFORMAT_UNDEFINED
;
422 rDB
.getValue(nCol
, aData
, nFormatType
);
423 aBuckets
.push_back(Bucket(aData
, 0, nRow
));
424 if (!aData
.IsEmpty())
426 maEmptyRows
.insert_back(nRow
, nRow
+1, false);
427 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
428 rField
.mnNumFormat
= pFormatter
? pFormatter
->GetStandardFormat(nFormatType
) : 0;
435 processBuckets(aBuckets
, rField
);
440 if (!maFields
.empty())
441 mnRowCount
= maFields
[0].maData
.size();
446 catch (const Exception
&)
452 bool ScDPCache::ValidQuery( SCROW nRow
, const ScQueryParam
&rParam
) const
454 if (!rParam
.GetEntryCount())
457 if (!rParam
.GetEntry(0).bDoQuery
)
460 bool bMatchWholeCell
= mpDoc
->GetDocOptions().IsMatchWholeCell();
462 SCSIZE nEntryCount
= rParam
.GetEntryCount();
463 std::vector
<bool> aPassed(nEntryCount
, false);
466 CollatorWrapper
* pCollator
= (rParam
.bCaseSens
? ScGlobal::GetCaseCollator() :
467 ScGlobal::GetCollator() );
468 ::utl::TransliterationWrapper
* pTransliteration
= (rParam
.bCaseSens
?
469 ScGlobal::GetCaseTransliteration() : ScGlobal::GetpTransliteration());
471 for (size_t i
= 0; i
< nEntryCount
&& rParam
.GetEntry(i
).bDoQuery
; ++i
)
473 const ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
474 const ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
475 // we can only handle one single direct query
476 // #i115431# nField in QueryParam is the sheet column, not the field within the source range
477 SCCOL nQueryCol
= (SCCOL
)rEntry
.nField
;
478 if ( nQueryCol
< rParam
.nCol1
)
479 nQueryCol
= rParam
.nCol1
;
480 if ( nQueryCol
> rParam
.nCol2
)
481 nQueryCol
= rParam
.nCol2
;
482 SCCOL nSourceField
= nQueryCol
- rParam
.nCol1
;
483 SCROW nId
= GetItemDataId( nSourceField
, nRow
, false );
484 const ScDPItemData
* pCellData
= GetItemDataById( nSourceField
, nId
);
488 if (rEntry
.GetQueryItem().meType
== ScQueryEntry::ByEmpty
)
490 if (rEntry
.IsQueryByEmpty())
491 bOk
= pCellData
->IsEmpty();
494 OSL_ASSERT(rEntry
.IsQueryByNonEmpty());
495 bOk
= !pCellData
->IsEmpty();
498 else if (rEntry
.GetQueryItem().meType
!= ScQueryEntry::ByString
&& pCellData
->IsValue())
500 double nCellVal
= pCellData
->GetValue();
505 bOk
= ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
508 bOk
= (nCellVal
< rItem
.mfVal
) && !::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
511 bOk
= (nCellVal
> rItem
.mfVal
) && !::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
514 bOk
= (nCellVal
< rItem
.mfVal
) || ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
516 case SC_GREATER_EQUAL
:
517 bOk
= (nCellVal
> rItem
.mfVal
) || ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
520 bOk
= !::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
527 else if ((rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
)
528 || (rEntry
.GetQueryItem().meType
== ScQueryEntry::ByString
529 && pCellData
->HasStringData() )
532 OUString aCellStr
= pCellData
->GetString();
534 bool bRealRegExp
= (rParam
.bRegExp
&& ((rEntry
.eOp
== SC_EQUAL
)
535 || (rEntry
.eOp
== SC_NOT_EQUAL
)));
536 bool bTestRegExp
= false;
537 if (bRealRegExp
|| bTestRegExp
)
539 sal_Int32 nStart
= 0;
540 sal_Int32 nEnd
= aCellStr
.getLength();
542 bool bMatch
= (bool) rEntry
.GetSearchTextPtr( rParam
.bCaseSens
)
543 ->SearchForward( aCellStr
, &nStart
, &nEnd
);
544 // from 614 on, nEnd is behind the found text
545 if (bMatch
&& bMatchWholeCell
546 && (nStart
!= 0 || nEnd
!= aCellStr
.getLength()))
547 bMatch
= false; // RegExp must match entire cell string
549 bOk
= ((rEntry
.eOp
== SC_NOT_EQUAL
) ? !bMatch
: bMatch
);
553 if (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
)
557 // TODO: Use shared string for fast equality check.
558 OUString aStr
= rEntry
.GetQueryItem().maString
.getString();
559 bOk
= pTransliteration
->isEqual(aCellStr
, aStr
);
560 bool bHasStar
= false;
562 if (( nIndex
= aStr
.indexOf('*') ) != -1)
564 if (bHasStar
&& (nIndex
>0))
566 for (sal_Int32 j
=0;(j
<nIndex
) && (j
< aCellStr
.getLength()) ; j
++)
568 if (aCellStr
[j
] == aStr
[j
])
582 OUString aQueryStr
= rEntry
.GetQueryItem().maString
.getString();
583 ::com::sun::star::uno::Sequence
< sal_Int32
> xOff
;
584 OUString aCell
= pTransliteration
->transliterate(
585 aCellStr
, ScGlobal::eLnge
, 0, aCellStr
.getLength(), &xOff
);
586 OUString aQuer
= pTransliteration
->transliterate(
587 aQueryStr
, ScGlobal::eLnge
, 0, aQueryStr
.getLength(), &xOff
);
588 bOk
= (aCell
.indexOf( aQuer
) != -1);
590 if (rEntry
.eOp
== SC_NOT_EQUAL
)
594 { // use collator here because data was probably sorted
595 sal_Int32 nCompare
= pCollator
->compareString(
596 aCellStr
, rEntry
.GetQueryItem().maString
.getString());
600 bOk
= (nCompare
< 0);
603 bOk
= (nCompare
> 0);
606 bOk
= (nCompare
<= 0);
608 case SC_GREATER_EQUAL
:
609 bOk
= (nCompare
>= 0);
612 OSL_FAIL("SC_NOT_EQUAL");
632 if (rEntry
.eConnect
== SC_AND
)
634 aPassed
[nPos
] = aPassed
[nPos
] && bOk
;
644 for (long j
=1; j
<= nPos
; j
++)
645 aPassed
[0] = aPassed
[0] || aPassed
[j
];
647 bool bRet
= aPassed
[0];
651 bool ScDPCache::IsRowEmpty(SCROW nRow
) const
654 maEmptyRows
.search_tree(nRow
, bEmpty
);
658 const ScDPCache::GroupItems
* ScDPCache::GetGroupItems(long nDim
) const
663 long nSourceCount
= static_cast<long>(maFields
.size());
664 if (nDim
< nSourceCount
)
665 return maFields
[nDim
].mpGroup
.get();
667 nDim
-= nSourceCount
;
668 if (nDim
< static_cast<long>(maGroupFields
.size()))
669 return &maGroupFields
[nDim
];
674 OUString
ScDPCache::GetDimensionName(LabelsType::size_type nDim
) const
676 OSL_ENSURE(nDim
< maLabelNames
.size()-1 , "ScDPTableDataCache::GetDimensionName");
677 OSL_ENSURE(maLabelNames
.size() == static_cast <sal_uInt16
> (mnColumnCount
+1), "ScDPTableDataCache::GetDimensionName");
679 if ( nDim
+1 < maLabelNames
.size() )
681 return maLabelNames
[nDim
+1];
689 typedef boost::unordered_set
<OUString
, OUStringHash
> LabelSet
;
691 class InsertLabel
: public std::unary_function
<OUString
, void>
695 InsertLabel(LabelSet
& rNames
) : mrNames(rNames
) {}
696 void operator() (const OUString
& r
)
704 void ScDPCache::PostInit()
706 OSL_ENSURE(!maFields
.empty(), "Cache not initialized!");
708 maEmptyRows
.build_tree();
709 typedef mdds::flat_segment_tree
<SCROW
, bool>::const_reverse_iterator itr_type
;
710 itr_type it
= maEmptyRows
.rbegin();
711 OSL_ENSURE(it
!= maEmptyRows
.rend(), "corrupt flat_segment_tree instance!");
712 mnDataSize
= maFields
[0].maData
.size();
713 ++it
; // Skip the first position.
714 OSL_ENSURE(it
!= maEmptyRows
.rend(), "buggy version of flat_segment_tree is used.");
717 SCROW nLastNonEmpty
= it
->first
- 1;
718 if (nLastNonEmpty
+1 < mnDataSize
)
719 mnDataSize
= nLastNonEmpty
+1;
723 void ScDPCache::Clear()
728 maLabelNames
.clear();
729 maGroupFields
.clear();
731 maStringPool
.clear();
734 void ScDPCache::AddLabel(const OUString
& rLabel
)
737 if ( maLabelNames
.empty() )
738 maLabelNames
.push_back(ScGlobal::GetRscString(STR_PIVOT_DATA
));
740 //reset name if needed
741 LabelSet aExistingNames
;
742 std::for_each(maLabelNames
.begin(), maLabelNames
.end(), InsertLabel(aExistingNames
));
743 sal_Int32 nSuffix
= 1;
744 OUString aNewName
= rLabel
;
747 if (!aExistingNames
.count(aNewName
))
749 // unique name found!
750 maLabelNames
.push_back(aNewName
);
754 // Name already exists.
755 OUStringBuffer
aBuf(rLabel
);
756 aBuf
.append(++nSuffix
);
757 aNewName
= aBuf
.makeStringAndClear();
761 SCROW
ScDPCache::GetItemDataId(sal_uInt16 nDim
, SCROW nRow
, bool bRepeatIfEmpty
) const
763 OSL_ENSURE(nDim
< mnColumnCount
, "ScDPTableDataCache::GetItemDataId ");
765 const Field
& rField
= maFields
[nDim
];
766 if (static_cast<size_t>(nRow
) >= rField
.maData
.size())
768 // nRow is in the trailing empty rows area.
770 nRow
= rField
.maData
.size()-1; // Move to the last non-empty row.
772 // Return the last item, which should always be empty if the
773 // initialization has skipped trailing empty rows.
774 return rField
.maItems
.size()-1;
777 else if (bRepeatIfEmpty
)
779 while (nRow
> 0 && rField
.maItems
[rField
.maData
[nRow
]].IsEmpty())
783 return rField
.maData
[nRow
];
786 const ScDPItemData
* ScDPCache::GetItemDataById(long nDim
, SCROW nId
) const
788 if (nDim
< 0 || nId
< 0)
791 size_t nSourceCount
= maFields
.size();
792 size_t nDimPos
= static_cast<size_t>(nDim
);
793 size_t nItemId
= static_cast<size_t>(nId
);
794 if (nDimPos
< nSourceCount
)
797 const Field
& rField
= maFields
[nDimPos
];
798 if (nItemId
< rField
.maItems
.size())
799 return &rField
.maItems
[nItemId
];
804 nItemId
-= rField
.maItems
.size();
805 const ItemsType
& rGI
= rField
.mpGroup
->maItems
;
806 if (nItemId
>= rGI
.size())
809 return &rGI
[nItemId
];
813 nDimPos
-= nSourceCount
;
814 if (nDimPos
>= maGroupFields
.size())
817 const ItemsType
& rGI
= maGroupFields
[nDimPos
].maItems
;
818 if (nItemId
>= rGI
.size())
821 return &rGI
[nItemId
];
824 SCROW
ScDPCache::GetRowCount() const
829 SCROW
ScDPCache::GetDataSize() const
831 OSL_ENSURE(mnDataSize
<= GetRowCount(), "Data size should never be larger than the row count.");
832 return mnDataSize
>= 0 ? mnDataSize
: 0;
835 const ScDPCache::ItemsType
& ScDPCache::GetDimMemberValues(SCCOL nDim
) const
837 OSL_ENSURE( nDim
>=0 && nDim
< mnColumnCount
," nDim < mnColumnCount ");
838 return maFields
.at(nDim
).maItems
;
841 sal_uLong
ScDPCache::GetNumberFormat( long nDim
) const
843 if ( nDim
>= mnColumnCount
)
846 // TODO: Find a way to determine the dominant number format in presence of
847 // multiple number formats in the same field.
848 return maFields
[nDim
].mnNumFormat
;
851 bool ScDPCache::IsDateDimension( long nDim
) const
853 if (nDim
>= mnColumnCount
)
856 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
860 short eType
= pFormatter
->GetType(maFields
[nDim
].mnNumFormat
);
861 return (eType
== NUMBERFORMAT_DATE
) || (eType
== NUMBERFORMAT_DATETIME
);
864 long ScDPCache::GetDimMemberCount(long nDim
) const
866 OSL_ENSURE( nDim
>=0 && nDim
< mnColumnCount
," ScDPTableDataCache::GetDimMemberCount : out of bound ");
867 return maFields
[nDim
].maItems
.size();
870 SCCOL
ScDPCache::GetDimensionIndex(const OUString
& sName
) const
872 for (size_t i
= 1; i
< maLabelNames
.size(); ++i
)
874 if (maLabelNames
[i
].equals(sName
))
875 return static_cast<SCCOL
>(i
-1);
880 const OUString
* ScDPCache::InternString(const OUString
& rStr
) const
882 StringSetType::iterator it
= maStringPool
.find(rStr
);
883 if (it
!= maStringPool
.end())
887 std::pair
<StringSetType::iterator
, bool> r
= maStringPool
.insert(rStr
);
888 return r
.second
? &(*r
.first
) : NULL
;
891 void ScDPCache::AddReference(ScDPObject
* pObj
) const
893 maRefObjects
.insert(pObj
);
896 void ScDPCache::RemoveReference(ScDPObject
* pObj
) const
899 // Object being deleted.
902 maRefObjects
.erase(pObj
);
903 if (maRefObjects
.empty())
904 mpDoc
->GetDPCollection()->RemoveCache(this);
907 const ScDPCache::ObjectSetType
& ScDPCache::GetAllReferences() const
912 SCROW
ScDPCache::GetIdByItemData(long nDim
, const ScDPItemData
& rItem
) const
917 if (nDim
< mnColumnCount
)
920 const ItemsType
& rItems
= maFields
[nDim
].maItems
;
921 for (size_t i
= 0, n
= rItems
.size(); i
< n
; ++i
)
923 if (rItems
[i
] == rItem
)
927 if (!maFields
[nDim
].mpGroup
)
930 // grouped source field.
931 const ItemsType
& rGI
= maFields
[nDim
].mpGroup
->maItems
;
932 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
935 return rItems
.size() + i
;
941 nDim
-= mnColumnCount
;
942 if (static_cast<size_t>(nDim
) < maGroupFields
.size())
944 const ItemsType
& rGI
= maGroupFields
[nDim
].maItems
;
945 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
955 OUString
ScDPCache::GetFormattedString(long nDim
, const ScDPItemData
& rItem
) const
958 return rItem
.GetString();
960 ScDPItemData::Type eType
= rItem
.GetType();
961 if (eType
== ScDPItemData::Value
)
963 // Format value using the stored number format.
964 sal_uLong nNumFormat
= GetNumberFormat(nDim
);
965 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
968 Color
* pColor
= NULL
;
970 pFormatter
->GetOutputString(rItem
.GetValue(), nNumFormat
, aStr
, &pColor
);
975 if (eType
== ScDPItemData::GroupValue
)
977 ScDPItemData::GroupValueAttr aAttr
= rItem
.GetGroupValue();
978 double fStart
= 0.0, fEnd
= 0.0;
979 const GroupItems
* p
= GetGroupItems(nDim
);
982 fStart
= p
->maInfo
.mfStart
;
983 fEnd
= p
->maInfo
.mfEnd
;
985 return ScDPUtil::getDateGroupName(
986 aAttr
.mnGroupType
, aAttr
.mnValue
, mpDoc
->GetFormatTable(), fStart
, fEnd
);
989 if (eType
== ScDPItemData::RangeStart
)
991 double fVal
= rItem
.GetValue();
992 const GroupItems
* p
= GetGroupItems(nDim
);
994 return rItem
.GetString();
996 sal_Unicode cDecSep
= ScGlobal::pLocaleData
->getNumDecimalSep()[0];
997 return ScDPUtil::getNumGroupName(fVal
, p
->maInfo
, cDecSep
, mpDoc
->GetFormatTable());
1000 return rItem
.GetString();
1003 long ScDPCache::AppendGroupField()
1005 maGroupFields
.push_back(new GroupItems
);
1006 return static_cast<long>(maFields
.size() + maGroupFields
.size() - 1);
1009 void ScDPCache::ResetGroupItems(long nDim
, const ScDPNumGroupInfo
& rNumInfo
, sal_Int32 nGroupType
)
1014 long nSourceCount
= static_cast<long>(maFields
.size());
1015 if (nDim
< nSourceCount
)
1017 maFields
.at(nDim
).mpGroup
.reset(new GroupItems(rNumInfo
, nGroupType
));
1021 nDim
-= nSourceCount
;
1022 if (nDim
< static_cast<long>(maGroupFields
.size()))
1024 GroupItems
& rGI
= maGroupFields
[nDim
];
1025 rGI
.maItems
.clear();
1026 rGI
.maInfo
= rNumInfo
;
1027 rGI
.mnGroupType
= nGroupType
;
1031 SCROW
ScDPCache::SetGroupItem(long nDim
, const ScDPItemData
& rData
)
1036 long nSourceCount
= static_cast<long>(maFields
.size());
1037 if (nDim
< nSourceCount
)
1039 GroupItems
& rGI
= *maFields
.at(nDim
).mpGroup
;
1040 rGI
.maItems
.push_back(rData
);
1041 SCROW nId
= maFields
[nDim
].maItems
.size() + rGI
.maItems
.size() - 1;
1045 nDim
-= nSourceCount
;
1046 if (nDim
< static_cast<long>(maGroupFields
.size()))
1048 ItemsType
& rItems
= maGroupFields
.at(nDim
).maItems
;
1049 rItems
.push_back(rData
);
1050 return rItems
.size()-1;
1056 void ScDPCache::GetGroupDimMemberIds(long nDim
, std::vector
<SCROW
>& rIds
) const
1061 long nSourceCount
= static_cast<long>(maFields
.size());
1062 if (nDim
< nSourceCount
)
1064 if (!maFields
.at(nDim
).mpGroup
)
1067 size_t nOffset
= maFields
[nDim
].maItems
.size();
1068 const ItemsType
& rGI
= maFields
[nDim
].mpGroup
->maItems
;
1069 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
1070 rIds
.push_back(static_cast<SCROW
>(i
+ nOffset
));
1075 nDim
-= nSourceCount
;
1076 if (nDim
< static_cast<long>(maGroupFields
.size()))
1078 const ItemsType
& rGI
= maGroupFields
.at(nDim
).maItems
;
1079 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
1080 rIds
.push_back(static_cast<SCROW
>(i
));
1086 struct ClearGroupItems
: std::unary_function
<ScDPCache::Field
, void>
1088 void operator() (ScDPCache::Field
& r
) const
1096 void ScDPCache::ClearGroupFields()
1098 maGroupFields
.clear();
1099 std::for_each(maFields
.begin(), maFields
.end(), ClearGroupItems());
1102 const ScDPNumGroupInfo
* ScDPCache::GetNumGroupInfo(long nDim
) const
1107 long nSourceCount
= static_cast<long>(maFields
.size());
1108 if (nDim
< nSourceCount
)
1110 if (!maFields
.at(nDim
).mpGroup
)
1113 return &maFields
[nDim
].mpGroup
->maInfo
;
1116 nDim
-= nSourceCount
;
1117 if (nDim
< static_cast<long>(maGroupFields
.size()))
1118 return &maGroupFields
.at(nDim
).maInfo
;
1123 sal_Int32
ScDPCache::GetGroupType(long nDim
) const
1128 long nSourceCount
= static_cast<long>(maFields
.size());
1129 if (nDim
< nSourceCount
)
1131 if (!maFields
.at(nDim
).mpGroup
)
1134 return maFields
[nDim
].mpGroup
->mnGroupType
;
1137 nDim
-= nSourceCount
;
1138 if (nDim
< static_cast<long>(maGroupFields
.size()))
1139 return maGroupFields
.at(nDim
).mnGroupType
;
1144 SCROW
ScDPCache::GetOrder(long /*nDim*/, SCROW nIndex
) const
1149 ScDocument
* ScDPCache::GetDoc() const
1154 long ScDPCache::GetColumnCount() const
1156 return mnColumnCount
;
1159 #if DEBUG_PIVOT_TABLE
1163 std::ostream
& operator<< (::std::ostream
& os
, const OUString
& str
)
1165 return os
<< OUStringToOString(str
, RTL_TEXTENCODING_UTF8
).getStr();
1168 void dumpItems(const ScDPCache
& rCache
, long nDim
, const ScDPCache::ItemsType
& rItems
, size_t nOffset
)
1170 for (size_t i
= 0; i
< rItems
.size(); ++i
)
1171 cout
<< " " << (i
+nOffset
) << ": " << rCache
.GetFormattedString(nDim
, rItems
[i
]) << endl
;
1174 void dumpSourceData(const ScDPCache
& rCache
, long nDim
, const ScDPCache::ItemsType
& rItems
, const ScDPCache::IndexArrayType
& rArray
)
1176 ScDPCache::IndexArrayType::const_iterator it
= rArray
.begin(), itEnd
= rArray
.end();
1177 for (; it
!= itEnd
; ++it
)
1178 cout
<< " '" << rCache
.GetFormattedString(nDim
, rItems
[*it
]) << "'" << endl
;
1181 const char* getGroupTypeName(sal_Int32 nType
)
1183 static const char* pNames
[] = {
1184 "", "years", "quarters", "months", "days", "hours", "minutes", "seconds"
1189 case sheet::DataPilotFieldGroupBy::YEARS
: return pNames
[1];
1190 case sheet::DataPilotFieldGroupBy::QUARTERS
: return pNames
[2];
1191 case sheet::DataPilotFieldGroupBy::MONTHS
: return pNames
[3];
1192 case sheet::DataPilotFieldGroupBy::DAYS
: return pNames
[4];
1193 case sheet::DataPilotFieldGroupBy::HOURS
: return pNames
[5];
1194 case sheet::DataPilotFieldGroupBy::MINUTES
: return pNames
[6];
1195 case sheet::DataPilotFieldGroupBy::SECONDS
: return pNames
[7];
1205 void ScDPCache::Dump() const
1207 // Change these flags to fit your debugging needs.
1208 bool bDumpItems
= false;
1209 bool bDumpSourceData
= false;
1211 cout
<< "--- pivot cache dump" << endl
;
1213 FieldsType::const_iterator it
= maFields
.begin(), itEnd
= maFields
.end();
1214 for (size_t i
= 0; it
!= itEnd
; ++it
, ++i
)
1216 const Field
& fld
= *it
;
1217 cout
<< "* source dimension: " << GetDimensionName(i
) << " (ID = " << i
<< ")" << endl
;
1218 cout
<< " item count: " << fld
.maItems
.size() << endl
;
1220 dumpItems(*this, i
, fld
.maItems
, 0);
1223 cout
<< " group item count: " << fld
.mpGroup
->maItems
.size() << endl
;
1224 cout
<< " group type: " << getGroupTypeName(fld
.mpGroup
->mnGroupType
) << endl
;
1226 dumpItems(*this, i
, fld
.mpGroup
->maItems
, fld
.maItems
.size());
1229 if (bDumpSourceData
)
1231 cout
<< " source data (re-constructed):" << endl
;
1232 dumpSourceData(*this, i
, fld
.maItems
, fld
.maData
);
1238 GroupFieldsType::const_iterator it
= maGroupFields
.begin(), itEnd
= maGroupFields
.end();
1239 for (size_t i
= maFields
.size(); it
!= itEnd
; ++it
, ++i
)
1241 const GroupItems
& gi
= *it
;
1242 cout
<< "* group dimension: (unnamed) (ID = " << i
<< ")" << endl
;
1243 cout
<< " item count: " << gi
.maItems
.size() << endl
;
1244 cout
<< " group type: " << getGroupTypeName(gi
.mnGroupType
) << endl
;
1246 dumpItems(*this, i
, gi
.maItems
, 0);
1251 struct { SCROW start
; SCROW end
; bool empty
; } aRange
;
1252 cout
<< "* empty rows: " << endl
;
1253 mdds::flat_segment_tree
<SCROW
, bool>::const_iterator it
= maEmptyRows
.begin(), itEnd
= maEmptyRows
.end();
1256 aRange
.start
= it
->first
;
1257 aRange
.empty
= it
->second
;
1261 for (; it
!= itEnd
; ++it
)
1263 aRange
.end
= it
->first
-1;
1264 cout
<< " rows " << aRange
.start
<< "-" << aRange
.end
<< ": " << (aRange
.empty
? "empty" : "not-empty") << endl
;
1265 aRange
.start
= it
->first
;
1266 aRange
.empty
= it
->second
;
1270 cout
<< "---" << endl
;
1275 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */