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/.
10 #include <pivot/PivotTableFormatOutput.hxx>
11 #include <pivot/DPOutLevelData.hxx>
13 #include <dpoutput.hxx>
14 #include <dpobject.hxx>
15 #include <dptabdat.hxx>
16 #include <dpcache.hxx>
17 #include <document.hxx>
19 #include <com/sun/star/sheet/MemberResultFlags.hpp>
28 ScDPTableData
& mrTableData
;
29 ScDPCache
const& mrCache
;
31 std::unordered_map
<sal_Int32
, std::vector
<OUString
>> maNameCache
;
33 void fillNamesForDimension(std::vector
<OUString
>& rNames
, sal_Int32 nDimension
)
35 for (const auto& rItemData
: mrCache
.GetDimMemberValues(nDimension
))
37 OUString sFormattedName
;
38 if (rItemData
.HasStringData() || rItemData
.IsEmpty())
39 sFormattedName
= rItemData
.GetString();
41 sFormattedName
= ScDPObject::GetFormattedString(&mrTableData
, nDimension
,
42 rItemData
.GetValue());
43 rNames
.push_back(sFormattedName
);
48 NameResolver(ScDPTableData
& rTableData
, ScDPCache
const& rCache
)
49 : mrTableData(rTableData
)
54 OUString
getNameForIndex(sal_uInt32 nIndex
, sal_Int32 nDimension
)
56 auto iterator
= maNameCache
.find(nDimension
);
57 if (iterator
== maNameCache
.end())
59 std::vector
<OUString
> aNames
;
60 fillNamesForDimension(aNames
, nDimension
);
61 iterator
= maNameCache
.emplace(nDimension
, aNames
).first
;
64 const std::vector
<OUString
>& rNames
= iterator
->second
;
65 if (nIndex
>= rNames
.size())
67 return rNames
[nIndex
];
71 void initLines(std::vector
<LineData
>& rLines
, std::vector
<ScDPOutLevelData
> const& rFields
)
73 for (size_t i
= 0; i
< rFields
.size(); i
++)
75 size_t nFieldLength(rFields
[i
].maResult
.getLength());
76 if (rLines
.size() < nFieldLength
)
77 rLines
.resize(nFieldLength
);
79 for (LineData
& rLineData
: rLines
)
81 rLineData
.maFields
.resize(rFields
.size());
86 Selection
const* findSelection(PivotTableFormat
const& rFormat
, tools::Long nDimension
)
88 for (Selection
const& rSelection
: rFormat
.getSelections())
90 if (rSelection
.nField
== nDimension
)
96 void fillOutputFieldFromSelection(FormatOutputField
& rOutputField
, Selection
const& rSelection
,
97 size_t nSelectionIndex
, NameResolver
& rNameResolver
)
99 if (rSelection
.nIndices
.empty())
101 rOutputField
.bMatchesAll
= true;
105 if (rSelection
.nIndices
.size() > 1 && rSelection
.nIndices
.size() > nSelectionIndex
)
106 rOutputField
.nIndex
= rSelection
.nIndices
[nSelectionIndex
];
108 rOutputField
.nIndex
= rSelection
.nIndices
[0];
110 if (rOutputField
.nDimension
== -2)
111 rOutputField
.aName
= "DATA";
114 = rNameResolver
.getNameForIndex(rOutputField
.nIndex
, rOutputField
.nDimension
);
116 rOutputField
.bSet
= true;
119 void initFormatOutputField(size_t nSelectionIndex
, std::vector
<FormatOutputField
>& rOutputFields
,
120 std::vector
<ScDPOutLevelData
> const& rFields
,
121 PivotTableFormat
const& rFormat
, NameResolver
& rNameResolver
)
123 rOutputFields
.resize(rFields
.size());
124 for (size_t i
= 0; i
< rOutputFields
.size(); i
++)
126 FormatOutputField
& rOutputField
= rOutputFields
[i
];
127 if (!rFields
[i
].mbDataLayout
)
128 rOutputField
.nDimension
= rFields
[i
].mnDim
;
130 Selection
const* pSelection
= findSelection(rFormat
, rOutputField
.nDimension
);
131 if (pSelection
== nullptr)
134 fillOutputFieldFromSelection(rOutputField
, *pSelection
, nSelectionIndex
, rNameResolver
);
138 } // end anonymous namespace
140 void FormatOutput::prepare(SCTAB nTab
, std::vector
<ScDPOutLevelData
> const& rColumnFields
,
141 std::vector
<ScDPOutLevelData
> const& rRowFields
,
142 bool bColumnFieldIsDataOnly
)
147 // initialize row and column lines, so the number of fields matches the pivot table output
148 initLines(maRowLines
, rRowFields
);
150 if (rColumnFields
.size() == 0 && bColumnFieldIsDataOnly
)
152 maColumnLines
.resize(1);
153 maColumnLines
[0].maFields
.resize(1);
157 initLines(maColumnLines
, rColumnFields
);
160 // check the table data exists
161 auto* pTableData
= mrObject
.GetTableData();
165 ScDPFilteredCache
const& rFilteredCache
= pTableData
->GetCacheTable();
166 ScDPCache
const& rCache
= rFilteredCache
.getCache();
168 NameResolver
aNameResolver(*pTableData
, rCache
);
170 // Initialize format output entries (FormatOutputEntry) and set the data already available from output fields
171 // (rColumnFields and rRowFields) and the pivot table format list (PivotTableFormat).
173 for (PivotTableFormat
const& rFormat
: mpFormats
->getVector())
175 size_t nMaxNumberOfIndices
= 1;
176 for (auto const& rSelection
: rFormat
.aSelections
)
178 if (rSelection
.nIndices
.size() > 1)
179 nMaxNumberOfIndices
= rSelection
.nIndices
.size();
182 for (size_t nSelectionIndex
= 0; nSelectionIndex
< nMaxNumberOfIndices
; nSelectionIndex
++)
184 sc::FormatOutputEntry aEntry
;
185 aEntry
.pPattern
= rFormat
.pPattern
;
187 aEntry
.eType
= rFormat
.eType
;
189 initFormatOutputField(nSelectionIndex
, aEntry
.aRowOutputFields
, rRowFields
, rFormat
,
192 // If column fields list is empty, but there is a data field in columns that is not part of column fields
193 if (rColumnFields
.size() == 0 && bColumnFieldIsDataOnly
)
195 // Initialize column output fields to have 1 data output field
196 aEntry
.aColumnOutputFields
.resize(1);
197 FormatOutputField
& rOutputField
= aEntry
.aColumnOutputFields
[0];
198 rOutputField
.nDimension
= -2;
199 Selection
const* pSelection
= findSelection(rFormat
, -2);
201 fillOutputFieldFromSelection(rOutputField
, *pSelection
, nSelectionIndex
,
206 initFormatOutputField(nSelectionIndex
, aEntry
.aColumnOutputFields
, rColumnFields
,
207 rFormat
, aNameResolver
);
210 maFormatOutputEntries
.push_back(aEntry
);
215 void FormatOutput::insertEmptyDataColumn(SCCOL nColPos
, SCROW nRowPos
)
220 LineData
& rLine
= maColumnLines
[0];
221 rLine
.oLine
= nColPos
;
222 rLine
.oPosition
= nRowPos
;
224 FieldData
& rFieldData
= rLine
.maFields
[0];
225 rFieldData
.nIndex
= 0;
226 rFieldData
.bIsSet
= true;
231 void fillLineAndFieldData(std::vector
<LineData
>& rLineDataVector
, size_t nFieldIndex
,
232 ScDPOutLevelData
const& rField
, tools::Long nMemberIndex
,
233 sheet::MemberResult
const& rMember
, SCCOLROW nLine
, SCCOLROW nPosition
)
235 LineData
& rLine
= rLineDataVector
[nMemberIndex
];
237 rLine
.oPosition
= nPosition
;
239 FieldData
& rFieldData
= rLine
.maFields
[nFieldIndex
];
240 if (!rField
.mbDataLayout
)
241 rFieldData
.mnDimension
= rField
.mnDim
;
242 rFieldData
.aName
= rMember
.Name
;
243 rFieldData
.nIndex
= nMemberIndex
;
244 rFieldData
.bIsSet
= true;
245 rFieldData
.bIsMember
= rMember
.Flags
& sheet::MemberResultFlags::HASMEMBER
;
246 rFieldData
.bSubtotal
= rMember
.Flags
& sheet::MemberResultFlags::SUBTOTAL
;
247 rFieldData
.bContinue
= rMember
.Flags
& sheet::MemberResultFlags::CONTINUE
;
249 // Search previous entries for the name / value
250 if (rFieldData
.bContinue
)
252 tools::Long nCurrent
= nMemberIndex
- 1;
253 while (nCurrent
>= 0 && rLineDataVector
[nCurrent
].maFields
[nFieldIndex
].bContinue
)
258 FieldData
& rCurrentFieldData
= rLineDataVector
[nCurrent
].maFields
[nFieldIndex
];
259 rFieldData
.aName
= rCurrentFieldData
.aName
;
260 rFieldData
.nIndex
= rCurrentFieldData
.nIndex
;
264 } // end anonymous namespace
266 void FormatOutput::insertFieldMember(size_t nFieldIndex
, ScDPOutLevelData
const& rField
,
267 tools::Long nMemberIndex
, sheet::MemberResult
const& rMember
,
268 SCCOL nColPos
, SCROW nRowPos
,
269 sc::FormatResultDirection eResultDirection
)
274 if (eResultDirection
== sc::FormatResultDirection::ROW
)
275 fillLineAndFieldData(maRowLines
, nFieldIndex
, rField
, nMemberIndex
, rMember
, nRowPos
,
277 else if (eResultDirection
== sc::FormatResultDirection::COLUMN
)
278 fillLineAndFieldData(maColumnLines
, nFieldIndex
, rField
, nMemberIndex
, rMember
, nColPos
,
283 void checkForMatchingLines(std::vector
<LineData
> const& rLines
,
284 std::vector
<FormatOutputField
> const& rFormatOutputField
,
286 std::vector
<std::reference_wrapper
<const LineData
>>& rMatches
,
287 std::vector
<std::reference_wrapper
<const LineData
>>& rMaybeMatches
)
289 for (LineData
const& rLineData
: rLines
)
292 size_t nMaybeMatch
= 0;
293 size_t nNoOfFields
= rLineData
.maFields
.size();
295 for (size_t nIndex
= 0; nIndex
< nNoOfFields
; nIndex
++)
297 FieldData
const& rFieldData
= rLineData
.maFields
[nIndex
];
298 FormatOutputField
const& rFormatEntry
= rFormatOutputField
[nIndex
];
299 bool bFieldMatch
= false;
300 bool bFieldMaybeMatch
= false;
302 tools::Long nDimension
= rFieldData
.mnDimension
;
303 if (nDimension
== rFormatEntry
.nDimension
)
305 if (rFormatEntry
.bSet
)
307 if (rFormatEntry
.bMatchesAll
&& !rFieldData
.bSubtotal
)
309 else if (nDimension
== -2 && rFieldData
.nIndex
== rFormatEntry
.nIndex
)
311 else if (nDimension
!= -2 && rFieldData
.aName
== rFormatEntry
.aName
)
314 else if (!rFormatEntry
.bSet
&& eType
== FormatType::Data
&& !rFieldData
.bIsMember
315 && !rFieldData
.bContinue
)
321 bFieldMaybeMatch
= true;
325 if (!bFieldMatch
&& !bFieldMaybeMatch
)
331 if (bFieldMaybeMatch
)
335 if (nMatch
== nNoOfFields
)
337 rMatches
.push_back(std::cref(rLineData
));
339 else if (nMatch
+ nMaybeMatch
== nNoOfFields
)
341 rMaybeMatches
.push_back(std::cref(rLineData
));
346 /** Check the lines in matches and maybe matches and output */
347 void evaluateMatches(ScDocument
& rDocument
,
348 std::vector
<std::reference_wrapper
<const LineData
>> const& rMatches
,
349 std::vector
<std::reference_wrapper
<const LineData
>> const& rMaybeMatches
,
350 std::vector
<SCCOLROW
>& aRows
, std::vector
<SCCOLROW
>& aColumns
,
351 FormatOutputEntry
const& rOutputEntry
, FormatResultDirection eResultDirection
)
353 // We expect that tab and pattern to be set or this method shouldn't be called at all
354 assert(rOutputEntry
.onTab
);
355 assert(rOutputEntry
.pPattern
);
357 if (rMatches
.empty() && rMaybeMatches
.empty())
360 bool bMaybeExists
= rMatches
.empty();
362 auto const& rLineDataVector
= bMaybeExists
? rMaybeMatches
: rMatches
;
364 for (LineData
const& rLineData
: rLineDataVector
)
366 // Can't continue if we don't have complete row/column data
367 if (!rLineData
.oLine
|| !rLineData
.oPosition
)
370 if (rOutputEntry
.eType
== FormatType::Label
&& !bMaybeExists
)
372 // Primary axis is set to column (line) then row (position)
373 SCCOLROW nColumn
= *rLineData
.oLine
;
374 SCCOLROW nRow
= *rLineData
.oPosition
;
376 // In row orientation, the primary axis is row, then column, so we need to swap
377 if (eResultDirection
== FormatResultDirection::ROW
)
378 std::swap(nRow
, nColumn
);
380 // Set the pattern to the sheet
381 rDocument
.ApplyPattern(nColumn
, nRow
, *rOutputEntry
.onTab
, *rOutputEntry
.pPattern
);
383 else if (rOutputEntry
.eType
== FormatType::Data
)
385 if (eResultDirection
== FormatResultDirection::ROW
)
386 aRows
.push_back(*rLineData
.oLine
);
387 else if (eResultDirection
== FormatResultDirection::COLUMN
)
388 aColumns
.push_back(*rLineData
.oLine
);
393 } // end anonymous namespace
395 void FormatOutput::apply(ScDocument
& rDocument
)
400 for (auto const& rOutputEntry
: maFormatOutputEntries
)
402 if (!rOutputEntry
.onTab
|| !rOutputEntry
.pPattern
)
405 std::vector
<SCCOLROW
> aRows
;
406 std::vector
<SCCOLROW
> aColumns
;
408 std::vector
<std::reference_wrapper
<const LineData
>> rMatches
;
409 std::vector
<std::reference_wrapper
<const LineData
>> rMaybeMatches
;
411 checkForMatchingLines(maRowLines
, rOutputEntry
.aRowOutputFields
, rOutputEntry
.eType
,
412 rMatches
, rMaybeMatches
);
414 evaluateMatches(rDocument
, rMatches
, rMaybeMatches
, aRows
, aColumns
, rOutputEntry
,
415 FormatResultDirection::ROW
);
419 std::vector
<std::reference_wrapper
<const LineData
>> rMatches
;
420 std::vector
<std::reference_wrapper
<const LineData
>> rMaybeMatches
;
422 checkForMatchingLines(maColumnLines
, rOutputEntry
.aColumnOutputFields
,
423 rOutputEntry
.eType
, rMatches
, rMaybeMatches
);
425 evaluateMatches(rDocument
, rMatches
, rMaybeMatches
, aRows
, aColumns
, rOutputEntry
,
426 FormatResultDirection::COLUMN
);
429 if (!aColumns
.empty() && !aRows
.empty() && rOutputEntry
.eType
== FormatType::Data
)
431 for (SCCOLROW nRow
: aRows
)
432 for (SCCOLROW nColumn
: aColumns
)
433 rDocument
.ApplyPattern(nColumn
, nRow
, *rOutputEntry
.onTab
,
434 *rOutputEntry
.pPattern
);
441 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */