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>
40 using namespace ::com::sun::star
;
42 using ::com::sun::star::uno::Exception
;
43 using ::com::sun::star::uno::Reference
;
44 using ::com::sun::star::uno::UNO_QUERY
;
45 using ::com::sun::star::uno::UNO_QUERY_THROW
;
47 ScDPCache::GroupItems::GroupItems() : mnGroupType(0) {}
49 ScDPCache::GroupItems::GroupItems(const ScDPNumGroupInfo
& rInfo
, sal_Int32 nGroupType
) :
50 maInfo(rInfo
), mnGroupType(nGroupType
) {}
52 ScDPCache::Field::Field() : mnNumFormat(0) {}
54 ScDPCache::ScDPCache(ScDocument
* pDoc
) :
57 maEmptyRows(0, MAXROW
, true),
66 struct ClearObjectSource
: std::unary_function
<ScDPObject
*, void>
68 void operator() (ScDPObject
* p
) const
76 ScDPCache::~ScDPCache()
78 // Make sure no live ScDPObject instances hold reference to this cache any
81 std::for_each(maRefObjects
.begin(), maRefObjects
.end(), ClearObjectSource());
87 * While the macro interpret level is incremented, the formula cells are
88 * (semi-)guaranteed to be interpreted.
90 class MacroInterpretIncrementer
93 MacroInterpretIncrementer(ScDocument
* pDoc
) :
96 mpDoc
->IncMacroInterpretLevel();
98 ~MacroInterpretIncrementer()
100 mpDoc
->DecMacroInterpretLevel();
106 OUString
createLabelString(ScDocument
* pDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
108 OUString aDocStr
= pDoc
->GetString(nCol
, nRow
, nTab
);
109 if (aDocStr
.isEmpty())
111 // Replace an empty label string with column name.
113 aBuf
.append(ScGlobal::GetRscString(STR_COLUMN
));
114 aBuf
.append(sal_Unicode(' '));
116 ScAddress
aColAddr(nCol
, 0, 0);
118 aColAddr
.Format(aColStr
, SCA_VALID_COL
, NULL
);
119 aBuf
.append(aColStr
);
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
= 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 String 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 xub_StrLen nStart
= 0;
538 xub_StrLen nEnd
= aCellStr
.Len();
539 bool bMatch
= (bool) rEntry
.GetSearchTextPtr( rParam
.bCaseSens
)
540 ->SearchFrwrd( aCellStr
, &nStart
, &nEnd
);
541 // from 614 on, nEnd is behind the found text
542 if (bMatch
&& bMatchWholeCell
543 && (nStart
!= 0 || nEnd
!= aCellStr
.Len()))
544 bMatch
= false; // RegExp must match entire cell string
546 bOk
= ((rEntry
.eOp
== SC_NOT_EQUAL
) ? !bMatch
: bMatch
);
550 if (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
)
554 String aStr
= rEntry
.GetQueryItem().maString
;
555 bOk
= pTransliteration
->isEqual(aCellStr
, aStr
);
556 bool bHasStar
= false;
558 if (( nIndex
= aStr
.Search('*') ) != STRING_NOTFOUND
)
560 if (bHasStar
&& (nIndex
>0))
562 for (i
=0;(i
<nIndex
) && (i
< aCellStr
.Len()) ; i
++)
564 if (aCellStr
.GetChar( (sal_uInt16
)i
) == aStr
.GetChar((sal_uInt16
) i
))
578 const OUString
& rQueryStr
= rEntry
.GetQueryItem().maString
;
579 ::com::sun::star::uno::Sequence
< sal_Int32
> xOff
;
580 String aCell
= pTransliteration
->transliterate(
581 aCellStr
, ScGlobal::eLnge
, 0, aCellStr
.Len(), &xOff
);
582 String aQuer
= pTransliteration
->transliterate(
583 rQueryStr
, ScGlobal::eLnge
, 0, rQueryStr
.getLength(), &xOff
);
584 bOk
= (aCell
.Search( aQuer
) != STRING_NOTFOUND
);
586 if (rEntry
.eOp
== SC_NOT_EQUAL
)
590 { // use collator here because data was probably sorted
591 sal_Int32 nCompare
= pCollator
->compareString(
592 aCellStr
, rEntry
.GetQueryItem().maString
);
596 bOk
= (nCompare
< 0);
599 bOk
= (nCompare
> 0);
602 bOk
= (nCompare
<= 0);
604 case SC_GREATER_EQUAL
:
605 bOk
= (nCompare
>= 0);
608 OSL_FAIL("SC_NOT_EQUAL");
628 if (rEntry
.eConnect
== SC_AND
)
630 aPassed
[nPos
] = aPassed
[nPos
] && bOk
;
640 for (long j
=1; j
<= nPos
; j
++)
641 aPassed
[0] = aPassed
[0] || aPassed
[j
];
643 bool bRet
= aPassed
[0];
647 bool ScDPCache::IsRowEmpty(SCROW nRow
) const
650 maEmptyRows
.search_tree(nRow
, bEmpty
);
654 const ScDPCache::GroupItems
* ScDPCache::GetGroupItems(long nDim
) const
659 long nSourceCount
= static_cast<long>(maFields
.size());
660 if (nDim
< nSourceCount
)
661 return maFields
[nDim
].mpGroup
.get();
663 nDim
-= nSourceCount
;
664 if (nDim
< static_cast<long>(maGroupFields
.size()))
665 return &maGroupFields
[nDim
];
670 OUString
ScDPCache::GetDimensionName(LabelsType::size_type nDim
) const
672 OSL_ENSURE(nDim
< maLabelNames
.size()-1 , "ScDPTableDataCache::GetDimensionName");
673 OSL_ENSURE(maLabelNames
.size() == static_cast <sal_uInt16
> (mnColumnCount
+1), "ScDPTableDataCache::GetDimensionName");
675 if ( nDim
+1 < maLabelNames
.size() )
677 return maLabelNames
[nDim
+1];
685 typedef boost::unordered_set
<OUString
, OUStringHash
> LabelSet
;
687 class InsertLabel
: public std::unary_function
<OUString
, void>
691 InsertLabel(LabelSet
& rNames
) : mrNames(rNames
) {}
692 void operator() (const OUString
& r
)
700 void ScDPCache::PostInit()
702 OSL_ENSURE(!maFields
.empty(), "Cache not initialized!");
704 maEmptyRows
.build_tree();
705 typedef mdds::flat_segment_tree
<SCROW
, bool>::const_reverse_iterator itr_type
;
706 itr_type it
= maEmptyRows
.rbegin();
707 OSL_ENSURE(it
!= maEmptyRows
.rend(), "corrupt flat_segment_tree instance!");
708 mnDataSize
= maFields
[0].maData
.size();
709 ++it
; // Skip the first position.
710 OSL_ENSURE(it
!= maEmptyRows
.rend(), "buggy version of flat_segment_tree is used.");
713 SCROW nLastNonEmpty
= it
->first
- 1;
714 if (nLastNonEmpty
+1 < mnDataSize
)
715 mnDataSize
= nLastNonEmpty
+1;
719 void ScDPCache::Clear()
724 maLabelNames
.clear();
725 maGroupFields
.clear();
727 maStringPool
.clear();
730 void ScDPCache::AddLabel(const OUString
& rLabel
)
733 if ( maLabelNames
.empty() )
734 maLabelNames
.push_back(ScGlobal::GetRscString(STR_PIVOT_DATA
));
736 //reset name if needed
737 LabelSet aExistingNames
;
738 std::for_each(maLabelNames
.begin(), maLabelNames
.end(), InsertLabel(aExistingNames
));
739 sal_Int32 nSuffix
= 1;
740 OUString aNewName
= rLabel
;
743 if (!aExistingNames
.count(aNewName
))
745 // unique name found!
746 maLabelNames
.push_back(aNewName
);
750 // Name already exists.
751 OUStringBuffer
aBuf(rLabel
);
752 aBuf
.append(++nSuffix
);
753 aNewName
= aBuf
.makeStringAndClear();
757 SCROW
ScDPCache::GetItemDataId(sal_uInt16 nDim
, SCROW nRow
, bool bRepeatIfEmpty
) const
759 OSL_ENSURE(nDim
< mnColumnCount
, "ScDPTableDataCache::GetItemDataId ");
761 const Field
& rField
= maFields
[nDim
];
762 if (static_cast<size_t>(nRow
) >= rField
.maData
.size())
764 // nRow is in the trailing empty rows area.
766 nRow
= rField
.maData
.size()-1; // Move to the last non-empty row.
768 // Return the last item, which should always be empty if the
769 // initialization has skipped trailing empty rows.
770 return rField
.maItems
.size()-1;
773 else if (bRepeatIfEmpty
)
775 while (nRow
> 0 && rField
.maItems
[rField
.maData
[nRow
]].IsEmpty())
779 return rField
.maData
[nRow
];
782 const ScDPItemData
* ScDPCache::GetItemDataById(long nDim
, SCROW nId
) const
784 if (nDim
< 0 || nId
< 0)
787 size_t nSourceCount
= maFields
.size();
788 size_t nDimPos
= static_cast<size_t>(nDim
);
789 size_t nItemId
= static_cast<size_t>(nId
);
790 if (nDimPos
< nSourceCount
)
793 const Field
& rField
= maFields
[nDimPos
];
794 if (nItemId
< rField
.maItems
.size())
795 return &rField
.maItems
[nItemId
];
800 nItemId
-= rField
.maItems
.size();
801 const ItemsType
& rGI
= rField
.mpGroup
->maItems
;
802 if (nItemId
>= rGI
.size())
805 return &rGI
[nItemId
];
809 nDimPos
-= nSourceCount
;
810 if (nDimPos
>= maGroupFields
.size())
813 const ItemsType
& rGI
= maGroupFields
[nDimPos
].maItems
;
814 if (nItemId
>= rGI
.size())
817 return &rGI
[nItemId
];
820 SCROW
ScDPCache::GetRowCount() const
825 SCROW
ScDPCache::GetDataSize() const
827 OSL_ENSURE(mnDataSize
<= GetRowCount(), "Data size should never be larger than the row count.");
828 return mnDataSize
>= 0 ? mnDataSize
: 0;
831 const ScDPCache::ItemsType
& ScDPCache::GetDimMemberValues(SCCOL nDim
) const
833 OSL_ENSURE( nDim
>=0 && nDim
< mnColumnCount
," nDim < mnColumnCount ");
834 return maFields
.at(nDim
).maItems
;
837 sal_uLong
ScDPCache::GetNumberFormat( long nDim
) const
839 if ( nDim
>= mnColumnCount
)
842 // TODO: Find a way to determine the dominant number format in presence of
843 // multiple number formats in the same field.
844 return maFields
[nDim
].mnNumFormat
;
847 bool ScDPCache::IsDateDimension( long nDim
) const
849 if (nDim
>= mnColumnCount
)
852 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
856 short eType
= pFormatter
->GetType(maFields
[nDim
].mnNumFormat
);
857 return (eType
== NUMBERFORMAT_DATE
) || (eType
== NUMBERFORMAT_DATETIME
);
860 long ScDPCache::GetDimMemberCount(long nDim
) const
862 OSL_ENSURE( nDim
>=0 && nDim
< mnColumnCount
," ScDPTableDataCache::GetDimMemberCount : out of bound ");
863 return maFields
[nDim
].maItems
.size();
866 SCCOL
ScDPCache::GetDimensionIndex(const OUString
& sName
) const
868 for (size_t i
= 1; i
< maLabelNames
.size(); ++i
)
870 if (maLabelNames
[i
].equals(sName
))
871 return static_cast<SCCOL
>(i
-1);
876 const OUString
* ScDPCache::InternString(const OUString
& rStr
) const
878 StringSetType::iterator it
= maStringPool
.find(rStr
);
879 if (it
!= maStringPool
.end())
883 std::pair
<StringSetType::iterator
, bool> r
= maStringPool
.insert(rStr
);
884 return r
.second
? &(*r
.first
) : NULL
;
887 void ScDPCache::AddReference(ScDPObject
* pObj
) const
889 maRefObjects
.insert(pObj
);
892 void ScDPCache::RemoveReference(ScDPObject
* pObj
) const
895 // Object being deleted.
898 maRefObjects
.erase(pObj
);
899 if (maRefObjects
.empty())
900 mpDoc
->GetDPCollection()->RemoveCache(this);
903 const ScDPCache::ObjectSetType
& ScDPCache::GetAllReferences() const
908 SCROW
ScDPCache::GetIdByItemData(long nDim
, const ScDPItemData
& rItem
) const
913 if (nDim
< mnColumnCount
)
916 const ItemsType
& rItems
= maFields
[nDim
].maItems
;
917 for (size_t i
= 0, n
= rItems
.size(); i
< n
; ++i
)
919 if (rItems
[i
] == rItem
)
923 if (!maFields
[nDim
].mpGroup
)
926 // grouped source field.
927 const ItemsType
& rGI
= maFields
[nDim
].mpGroup
->maItems
;
928 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
931 return rItems
.size() + i
;
937 nDim
-= mnColumnCount
;
938 if (static_cast<size_t>(nDim
) < maGroupFields
.size())
940 const ItemsType
& rGI
= maGroupFields
[nDim
].maItems
;
941 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
951 OUString
ScDPCache::GetFormattedString(long nDim
, const ScDPItemData
& rItem
) const
954 return rItem
.GetString();
956 ScDPItemData::Type eType
= rItem
.GetType();
957 if (eType
== ScDPItemData::Value
)
959 // Format value using the stored number format.
960 sal_uLong nNumFormat
= GetNumberFormat(nDim
);
961 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
964 Color
* pColor
= NULL
;
966 pFormatter
->GetOutputString(rItem
.GetValue(), nNumFormat
, aStr
, &pColor
);
971 if (eType
== ScDPItemData::GroupValue
)
973 ScDPItemData::GroupValueAttr aAttr
= rItem
.GetGroupValue();
974 double fStart
= 0.0, fEnd
= 0.0;
975 const GroupItems
* p
= GetGroupItems(nDim
);
978 fStart
= p
->maInfo
.mfStart
;
979 fEnd
= p
->maInfo
.mfEnd
;
981 return ScDPUtil::getDateGroupName(
982 aAttr
.mnGroupType
, aAttr
.mnValue
, mpDoc
->GetFormatTable(), fStart
, fEnd
);
985 if (eType
== ScDPItemData::RangeStart
)
987 double fVal
= rItem
.GetValue();
988 const GroupItems
* p
= GetGroupItems(nDim
);
990 return rItem
.GetString();
992 sal_Unicode cDecSep
= ScGlobal::pLocaleData
->getNumDecimalSep()[0];
993 return ScDPUtil::getNumGroupName(fVal
, p
->maInfo
, cDecSep
, mpDoc
->GetFormatTable());
996 return rItem
.GetString();
999 long ScDPCache::AppendGroupField()
1001 maGroupFields
.push_back(new GroupItems
);
1002 return static_cast<long>(maFields
.size() + maGroupFields
.size() - 1);
1005 void ScDPCache::ResetGroupItems(long nDim
, const ScDPNumGroupInfo
& rNumInfo
, sal_Int32 nGroupType
)
1010 long nSourceCount
= static_cast<long>(maFields
.size());
1011 if (nDim
< nSourceCount
)
1013 maFields
.at(nDim
).mpGroup
.reset(new GroupItems(rNumInfo
, nGroupType
));
1017 nDim
-= nSourceCount
;
1018 if (nDim
< static_cast<long>(maGroupFields
.size()))
1020 GroupItems
& rGI
= maGroupFields
[nDim
];
1021 rGI
.maItems
.clear();
1022 rGI
.maInfo
= rNumInfo
;
1023 rGI
.mnGroupType
= nGroupType
;
1027 SCROW
ScDPCache::SetGroupItem(long nDim
, const ScDPItemData
& rData
)
1032 long nSourceCount
= static_cast<long>(maFields
.size());
1033 if (nDim
< nSourceCount
)
1035 GroupItems
& rGI
= *maFields
.at(nDim
).mpGroup
;
1036 rGI
.maItems
.push_back(rData
);
1037 SCROW nId
= maFields
[nDim
].maItems
.size() + rGI
.maItems
.size() - 1;
1041 nDim
-= nSourceCount
;
1042 if (nDim
< static_cast<long>(maGroupFields
.size()))
1044 ItemsType
& rItems
= maGroupFields
.at(nDim
).maItems
;
1045 rItems
.push_back(rData
);
1046 return rItems
.size()-1;
1052 void ScDPCache::GetGroupDimMemberIds(long nDim
, std::vector
<SCROW
>& rIds
) const
1057 long nSourceCount
= static_cast<long>(maFields
.size());
1058 if (nDim
< nSourceCount
)
1060 if (!maFields
.at(nDim
).mpGroup
)
1063 size_t nOffset
= maFields
[nDim
].maItems
.size();
1064 const ItemsType
& rGI
= maFields
[nDim
].mpGroup
->maItems
;
1065 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
1066 rIds
.push_back(static_cast<SCROW
>(i
+ nOffset
));
1071 nDim
-= nSourceCount
;
1072 if (nDim
< static_cast<long>(maGroupFields
.size()))
1074 const ItemsType
& rGI
= maGroupFields
.at(nDim
).maItems
;
1075 for (size_t i
= 0, n
= rGI
.size(); i
< n
; ++i
)
1076 rIds
.push_back(static_cast<SCROW
>(i
));
1082 struct ClearGroupItems
: std::unary_function
<ScDPCache::Field
, void>
1084 void operator() (ScDPCache::Field
& r
) const
1092 void ScDPCache::ClearGroupFields()
1094 maGroupFields
.clear();
1095 std::for_each(maFields
.begin(), maFields
.end(), ClearGroupItems());
1098 const ScDPNumGroupInfo
* ScDPCache::GetNumGroupInfo(long nDim
) const
1103 long nSourceCount
= static_cast<long>(maFields
.size());
1104 if (nDim
< nSourceCount
)
1106 if (!maFields
.at(nDim
).mpGroup
)
1109 return &maFields
[nDim
].mpGroup
->maInfo
;
1112 nDim
-= nSourceCount
;
1113 if (nDim
< static_cast<long>(maGroupFields
.size()))
1114 return &maGroupFields
.at(nDim
).maInfo
;
1119 sal_Int32
ScDPCache::GetGroupType(long nDim
) const
1124 long nSourceCount
= static_cast<long>(maFields
.size());
1125 if (nDim
< nSourceCount
)
1127 if (!maFields
.at(nDim
).mpGroup
)
1130 return maFields
[nDim
].mpGroup
->mnGroupType
;
1133 nDim
-= nSourceCount
;
1134 if (nDim
< static_cast<long>(maGroupFields
.size()))
1135 return maGroupFields
.at(nDim
).mnGroupType
;
1140 SCROW
ScDPCache::GetOrder(long /*nDim*/, SCROW nIndex
) const
1145 ScDocument
* ScDPCache::GetDoc() const
1150 long ScDPCache::GetColumnCount() const
1152 return mnColumnCount
;
1155 #if DEBUG_PIVOT_TABLE
1159 std::ostream
& operator<< (::std::ostream
& os
, const OUString
& str
)
1161 return os
<< OUStringToOString(str
, RTL_TEXTENCODING_UTF8
).getStr();
1164 void dumpItems(const ScDPCache
& rCache
, long nDim
, const ScDPCache::ItemsType
& rItems
, size_t nOffset
)
1166 for (size_t i
= 0; i
< rItems
.size(); ++i
)
1167 cout
<< " " << (i
+nOffset
) << ": " << rCache
.GetFormattedString(nDim
, rItems
[i
]) << endl
;
1170 void dumpSourceData(const ScDPCache
& rCache
, long nDim
, const ScDPCache::ItemsType
& rItems
, const ScDPCache::IndexArrayType
& rArray
)
1172 ScDPCache::IndexArrayType::const_iterator it
= rArray
.begin(), itEnd
= rArray
.end();
1173 for (; it
!= itEnd
; ++it
)
1174 cout
<< " '" << rCache
.GetFormattedString(nDim
, rItems
[*it
]) << "'" << endl
;
1179 void ScDPCache::Dump() const
1181 cout
<< "--- pivot cache dump" << endl
;
1183 FieldsType::const_iterator it
= maFields
.begin(), itEnd
= maFields
.end();
1184 for (size_t i
= 0; it
!= itEnd
; ++it
, ++i
)
1186 const Field
& fld
= *it
;
1187 cout
<< "* source dimension: " << GetDimensionName(i
) << " (ID = " << i
<< ")" << endl
;
1188 cout
<< " item count: " << fld
.maItems
.size() << endl
;
1189 dumpItems(*this, i
, fld
.maItems
, 0);
1192 cout
<< " group item count: " << fld
.mpGroup
->maItems
.size() << endl
;
1193 dumpItems(*this, i
, fld
.mpGroup
->maItems
, fld
.maItems
.size());
1196 cout
<< " source data (re-constructed):" << endl
;
1197 dumpSourceData(*this, i
, fld
.maItems
, fld
.maData
);
1202 struct { SCROW start
; SCROW end
; bool empty
; } aRange
;
1203 cout
<< "* empty rows: " << endl
;
1204 mdds::flat_segment_tree
<SCROW
, bool>::const_iterator it
= maEmptyRows
.begin(), itEnd
= maEmptyRows
.end();
1207 aRange
.start
= it
->first
;
1208 aRange
.empty
= it
->second
;
1212 for (; it
!= itEnd
; ++it
)
1214 aRange
.end
= it
->first
-1;
1215 cout
<< " rows " << aRange
.start
<< "-" << aRange
.end
<< ": " << (aRange
.empty
? "empty" : "not-empty") << endl
;
1216 aRange
.start
= it
->first
;
1217 aRange
.empty
= it
->second
;
1222 GroupFieldsType::const_iterator it
= maGroupFields
.begin(), itEnd
= maGroupFields
.end();
1223 for (size_t i
= maFields
.size(); it
!= itEnd
; ++it
, ++i
)
1225 const GroupItems
& gi
= *it
;
1226 cout
<< "* group dimension: (unnamed) (ID = " << i
<< ")" << endl
;
1227 cout
<< " item count: " << gi
.maItems
.size() << endl
;
1228 dumpItems(*this, i
, gi
.maItems
, 0);
1232 cout
<< "---" << endl
;
1237 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */