Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / data / dpcache.cxx
blob49e5d8f164e018b5b8746e2ba855769dc8494e9b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
30 #include "dputil.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>
38 #include <memory>
40 #if DEBUG_PIVOT_TABLE
41 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
42 #endif
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) :
59 mpDoc( pDoc ),
60 mnColumnCount ( 0 ),
61 maEmptyRows(0, MAXROW, true),
62 mnDataSize(-1),
63 mnRowCount(0),
64 mbDisposing(false)
68 namespace {
70 struct ClearObjectSource : std::unary_function<ScDPObject*, void>
72 void operator() (ScDPObject* p) const
74 p->ClearTableData();
80 ScDPCache::~ScDPCache()
82 // Make sure no live ScDPObject instances hold reference to this cache any
83 // more.
84 mbDisposing = true;
85 std::for_each(maRefObjects.begin(), maRefObjects.end(), ClearObjectSource());
88 namespace {
90 /**
91 * While the macro interpret level is incremented, the formula cells are
92 * (semi-)guaranteed to be interpreted.
94 class MacroInterpretIncrementer
96 public:
97 MacroInterpretIncrementer(ScDocument* pDoc) :
98 mpDoc(pDoc)
100 mpDoc->IncMacroInterpretLevel();
102 ~MacroInterpretIncrementer()
104 mpDoc->DecMacroInterpretLevel();
106 private:
107 ScDocument* mpDoc;
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.
116 OUStringBuffer aBuf;
117 aBuf.append(ScGlobal::GetRscString(STR_COLUMN));
118 aBuf.append(' ');
120 ScAddress aColAddr(nCol, 0, 0);
121 aBuf.append(aColAddr.Format(SCA_VALID_COL, NULL));
122 aDocStr = aBuf.makeStringAndClear();
124 return aDocStr;
127 void initFromCell(
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);
132 rNumFormat = 0;
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));
150 else
151 rData.SetEmpty();
154 struct Bucket
156 ScDPItemData maValue;
157 SCROW mnOrderIndex;
158 SCROW mnDataIndex;
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
165 #include <iostream>
166 using std::cout;
167 using std::endl;
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;
177 #endif
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;
214 public:
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;
225 public:
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>
235 SCROW mnCurIndex;
236 public:
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())
247 return;
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.
257 SCROW nCurIndex = 0;
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))
264 ++nCurIndex;
266 it->mnOrderIndex = nCurIndex;
267 aPrev = it->maValue;
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());
281 // Unique by value.
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)
296 Clear();
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();
307 // Sanity check
308 if (!ValidRow(nStartRow) || !ValidRow(nEndRow) || nEndRow <= nStartRow)
309 return false;
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.
325 nEndRow = nRow2;
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.
332 Clear();
333 return false;
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);
342 ScDPItemData aData;
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);
362 if (nNumFormat)
363 // Only take non-default number format.
364 rField.mnNumFormat = nNumFormat;
368 processBuckets(aBuckets, rField);
370 if (bTailEmptyRows)
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())
376 aData.SetEmpty();
377 rField.maItems.push_back(aData);
382 PostInit();
383 return true;
386 bool ScDPCache::InitFromDataBase(DBConnector& rDB)
388 Clear();
392 mnColumnCount = rDB.getColumnCount();
393 maFields.clear();
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);
405 AddLabel(aColTitle);
408 std::vector<Bucket> aBuckets;
409 ScDPItemData aData;
410 for (sal_Int32 nCol = 0; nCol < mnColumnCount; ++nCol)
412 if (!rDB.first())
413 continue;
415 aBuckets.clear();
416 Field& rField = maFields[nCol];
417 SCROW nRow = 0;
420 short nFormatType = NUMBERFORMAT_UNDEFINED;
421 aData.SetEmpty();
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;
431 ++nRow;
433 while (rDB.next());
435 processBuckets(aBuckets, rField);
438 rDB.finish();
440 if (!maFields.empty())
441 mnRowCount = maFields[0].maData.size();
443 PostInit();
444 return true;
446 catch (const Exception&)
448 return false;
452 bool ScDPCache::ValidQuery( SCROW nRow, const ScQueryParam &rParam) const
454 if (!rParam.GetEntryCount())
455 return true;
457 if (!rParam.GetEntry(0).bDoQuery)
458 return true;
460 bool bMatchWholeCell = mpDoc->GetDocOptions().IsMatchWholeCell();
462 SCSIZE nEntryCount = rParam.GetEntryCount();
463 std::vector<bool> aPassed(nEntryCount, false);
465 long nPos = -1;
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 );
486 bool bOk = false;
488 if (rEntry.GetQueryItem().meType == ScQueryEntry::ByEmpty)
490 if (rEntry.IsQueryByEmpty())
491 bOk = pCellData->IsEmpty();
492 else
494 OSL_ASSERT(rEntry.IsQueryByNonEmpty());
495 bOk = !pCellData->IsEmpty();
498 else if (rEntry.GetQueryItem().meType != ScQueryEntry::ByString && pCellData->IsValue())
499 { // by Value
500 double nCellVal = pCellData->GetValue();
502 switch (rEntry.eOp)
504 case SC_EQUAL :
505 bOk = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
506 break;
507 case SC_LESS :
508 bOk = (nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
509 break;
510 case SC_GREATER :
511 bOk = (nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
512 break;
513 case SC_LESS_EQUAL :
514 bOk = (nCellVal < rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
515 break;
516 case SC_GREATER_EQUAL :
517 bOk = (nCellVal > rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
518 break;
519 case SC_NOT_EQUAL :
520 bOk = !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
521 break;
522 default:
523 bOk= false;
524 break;
527 else if ((rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
528 || (rEntry.GetQueryItem().meType == ScQueryEntry::ByString
529 && pCellData->HasStringData() )
531 { // by String
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
548 if (bRealRegExp)
549 bOk = ((rEntry.eOp == SC_NOT_EQUAL) ? !bMatch : bMatch);
551 if (!bRealRegExp)
553 if (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
555 if (bMatchWholeCell)
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;
561 sal_Int32 nIndex;
562 if (( nIndex = aStr.indexOf('*') ) != -1)
563 bHasStar = sal_True;
564 if (bHasStar && (nIndex>0))
566 for (sal_Int32 j=0;(j<nIndex) && (j< aCellStr.getLength()) ; j++)
568 if (aCellStr[j] == aStr[j])
570 bOk=1;
572 else
574 bOk=0;
575 break;
580 else
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)
591 bOk = !bOk;
593 else
594 { // use collator here because data was probably sorted
595 sal_Int32 nCompare = pCollator->compareString(
596 aCellStr, rEntry.GetQueryItem().maString.getString());
597 switch (rEntry.eOp)
599 case SC_LESS :
600 bOk = (nCompare < 0);
601 break;
602 case SC_GREATER :
603 bOk = (nCompare > 0);
604 break;
605 case SC_LESS_EQUAL :
606 bOk = (nCompare <= 0);
607 break;
608 case SC_GREATER_EQUAL :
609 bOk = (nCompare >= 0);
610 break;
611 case SC_NOT_EQUAL:
612 OSL_FAIL("SC_NOT_EQUAL");
613 break;
614 case SC_TOPVAL:
615 case SC_BOTVAL:
616 case SC_TOPPERC:
617 case SC_BOTPERC:
618 default:
619 break;
625 if (nPos == -1)
627 nPos++;
628 aPassed[nPos] = bOk;
630 else
632 if (rEntry.eConnect == SC_AND)
634 aPassed[nPos] = aPassed[nPos] && bOk;
636 else
638 nPos++;
639 aPassed[nPos] = bOk;
644 for (long j=1; j <= nPos; j++)
645 aPassed[0] = aPassed[0] || aPassed[j];
647 bool bRet = aPassed[0];
648 return bRet;
651 bool ScDPCache::IsRowEmpty(SCROW nRow) const
653 bool bEmpty = true;
654 maEmptyRows.search_tree(nRow, bEmpty);
655 return bEmpty;
658 const ScDPCache::GroupItems* ScDPCache::GetGroupItems(long nDim) const
660 if (nDim < 0)
661 return NULL;
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];
671 return NULL;
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];
683 else
684 return OUString();
687 namespace {
689 typedef boost::unordered_set<OUString, OUStringHash> LabelSet;
691 class InsertLabel : public std::unary_function<OUString, void>
693 LabelSet& mrNames;
694 public:
695 InsertLabel(LabelSet& rNames) : mrNames(rNames) {}
696 void operator() (const OUString& r)
698 mrNames.insert(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.");
715 if (it->second)
717 SCROW nLastNonEmpty = it->first - 1;
718 if (nLastNonEmpty+1 < mnDataSize)
719 mnDataSize = nLastNonEmpty+1;
723 void ScDPCache::Clear()
725 mnColumnCount = 0;
726 mnRowCount = 0;
727 maFields.clear();
728 maLabelNames.clear();
729 maGroupFields.clear();
730 maEmptyRows.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;
745 while (true)
747 if (!aExistingNames.count(aNewName))
749 // unique name found!
750 maLabelNames.push_back(aNewName);
751 return;
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.
769 if (bRepeatIfEmpty)
770 nRow = rField.maData.size()-1; // Move to the last non-empty row.
771 else
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())
780 --nRow;
783 return rField.maData[nRow];
786 const ScDPItemData* ScDPCache::GetItemDataById(long nDim, SCROW nId) const
788 if (nDim < 0 || nId < 0)
789 return NULL;
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)
796 // source field.
797 const Field& rField = maFields[nDimPos];
798 if (nItemId < rField.maItems.size())
799 return &rField.maItems[nItemId];
801 if (!rField.mpGroup)
802 return NULL;
804 nItemId -= rField.maItems.size();
805 const ItemsType& rGI = rField.mpGroup->maItems;
806 if (nItemId >= rGI.size())
807 return NULL;
809 return &rGI[nItemId];
812 // Try group fields.
813 nDimPos -= nSourceCount;
814 if (nDimPos >= maGroupFields.size())
815 return NULL;
817 const ItemsType& rGI = maGroupFields[nDimPos].maItems;
818 if (nItemId >= rGI.size())
819 return NULL;
821 return &rGI[nItemId];
824 SCROW ScDPCache::GetRowCount() const
826 return mnRowCount;
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 )
844 return 0;
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)
854 return false;
856 SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
857 if (!pFormatter)
858 return false;
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);
877 return -1;
880 const OUString* ScDPCache::InternString(const OUString& rStr) const
882 StringSetType::iterator it = maStringPool.find(rStr);
883 if (it != maStringPool.end())
884 // In the pool.
885 return &(*it);
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
898 if (mbDisposing)
899 // Object being deleted.
900 return;
902 maRefObjects.erase(pObj);
903 if (maRefObjects.empty())
904 mpDoc->GetDPCollection()->RemoveCache(this);
907 const ScDPCache::ObjectSetType& ScDPCache::GetAllReferences() const
909 return maRefObjects;
912 SCROW ScDPCache::GetIdByItemData(long nDim, const ScDPItemData& rItem) const
914 if (nDim < 0)
915 return -1;
917 if (nDim < mnColumnCount)
919 // source field.
920 const ItemsType& rItems = maFields[nDim].maItems;
921 for (size_t i = 0, n = rItems.size(); i < n; ++i)
923 if (rItems[i] == rItem)
924 return i;
927 if (!maFields[nDim].mpGroup)
928 return -1;
930 // grouped source field.
931 const ItemsType& rGI = maFields[nDim].mpGroup->maItems;
932 for (size_t i = 0, n = rGI.size(); i < n; ++i)
934 if (rGI[i] == rItem)
935 return rItems.size() + i;
937 return -1;
940 // group field.
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)
947 if (rGI[i] == rItem)
948 return i;
952 return -1;
955 OUString ScDPCache::GetFormattedString(long nDim, const ScDPItemData& rItem) const
957 if (nDim < 0)
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();
966 if (pFormatter)
968 Color* pColor = NULL;
969 OUString aStr;
970 pFormatter->GetOutputString(rItem.GetValue(), nNumFormat, aStr, &pColor);
971 return aStr;
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);
980 if (p)
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);
993 if (!p)
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)
1011 if (nDim < 0)
1012 return;
1014 long nSourceCount = static_cast<long>(maFields.size());
1015 if (nDim < nSourceCount)
1017 maFields.at(nDim).mpGroup.reset(new GroupItems(rNumInfo, nGroupType));
1018 return;
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)
1033 if (nDim < 0)
1034 return -1;
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;
1042 return nId;
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;
1053 return -1;
1056 void ScDPCache::GetGroupDimMemberIds(long nDim, std::vector<SCROW>& rIds) const
1058 if (nDim < 0)
1059 return;
1061 long nSourceCount = static_cast<long>(maFields.size());
1062 if (nDim < nSourceCount)
1064 if (!maFields.at(nDim).mpGroup)
1065 return;
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));
1072 return;
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));
1084 namespace {
1086 struct ClearGroupItems : std::unary_function<ScDPCache::Field, void>
1088 void operator() (ScDPCache::Field& r) const
1090 r.mpGroup.reset();
1096 void ScDPCache::ClearGroupFields()
1098 maGroupFields.clear();
1099 std::for_each(maFields.begin(), maFields.end(), ClearGroupItems());
1102 const ScDPNumGroupInfo* ScDPCache::GetNumGroupInfo(long nDim) const
1104 if (nDim < 0)
1105 return NULL;
1107 long nSourceCount = static_cast<long>(maFields.size());
1108 if (nDim < nSourceCount)
1110 if (!maFields.at(nDim).mpGroup)
1111 return NULL;
1113 return &maFields[nDim].mpGroup->maInfo;
1116 nDim -= nSourceCount;
1117 if (nDim < static_cast<long>(maGroupFields.size()))
1118 return &maGroupFields.at(nDim).maInfo;
1120 return NULL;
1123 sal_Int32 ScDPCache::GetGroupType(long nDim) const
1125 if (nDim < 0)
1126 return 0;
1128 long nSourceCount = static_cast<long>(maFields.size());
1129 if (nDim < nSourceCount)
1131 if (!maFields.at(nDim).mpGroup)
1132 return 0;
1134 return maFields[nDim].mpGroup->mnGroupType;
1137 nDim -= nSourceCount;
1138 if (nDim < static_cast<long>(maGroupFields.size()))
1139 return maGroupFields.at(nDim).mnGroupType;
1141 return 0;
1144 SCROW ScDPCache::GetOrder(long /*nDim*/, SCROW nIndex) const
1146 return nIndex;
1149 ScDocument* ScDPCache::GetDoc() const
1151 return mpDoc;
1154 long ScDPCache::GetColumnCount() const
1156 return mnColumnCount;
1159 #if DEBUG_PIVOT_TABLE
1161 namespace {
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"
1187 switch (nType)
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];
1196 default:
1200 return pNames[0];
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;
1219 if (bDumpItems)
1220 dumpItems(*this, i, fld.maItems, 0);
1221 if (fld.mpGroup)
1223 cout << " group item count: " << fld.mpGroup->maItems.size() << endl;
1224 cout << " group type: " << getGroupTypeName(fld.mpGroup->mnGroupType) << endl;
1225 if (bDumpItems)
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;
1245 if (bDumpItems)
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();
1254 if (it != itEnd)
1256 aRange.start = it->first;
1257 aRange.empty = it->second;
1258 ++it;
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;
1273 #endif
1275 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */