1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <dpcache.hxx>
21 #include <dpdimsave.hxx>
22 #include <dpgroup.hxx>
23 #include <dpobject.hxx>
24 #include <dputil.hxx>
25 #include <document.hxx>
26
27 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
28
29 #include <svl/zforlist.hxx>
30 #include <osl/diagnose.h>
31 #include <rtl/math.hxx>
32 #include <algorithm>
33
34 #include <globstr.hrc>
35 #include <scresid.hxx>
36
37 using namespace com::sun::star;
38
ScDPSaveGroupItem(const OUString & rName)39 ScDPSaveGroupItem::ScDPSaveGroupItem( const OUString& rName ) :
40 aGroupName(rName) {}
41
~ScDPSaveGroupItem()42 ScDPSaveGroupItem::~ScDPSaveGroupItem() {}
43
AddElement(const OUString & rName)44 void ScDPSaveGroupItem::AddElement( const OUString& rName )
45 {
46 aElements.push_back(rName);
47 }
48
AddElementsFromGroup(const ScDPSaveGroupItem & rGroup)49 void ScDPSaveGroupItem::AddElementsFromGroup( const ScDPSaveGroupItem& rGroup )
50 {
51 // add all elements of the other group (used for nested grouping)
52
53 for ( const auto& rElement : rGroup.aElements )
54 aElements.push_back( rElement );
55 }
56
RemoveElement(const OUString & rName)57 bool ScDPSaveGroupItem::RemoveElement( const OUString& rName )
58 {
59 auto it = std::find(aElements.begin(), aElements.end(), rName); //TODO: ignore case
60 if (it != aElements.end())
61 {
62 aElements.erase(it);
63 return true;
64 }
65 return false; // not found
66 }
67
IsEmpty() const68 bool ScDPSaveGroupItem::IsEmpty() const
69 {
70 return aElements.empty();
71 }
72
GetElementCount() const73 size_t ScDPSaveGroupItem::GetElementCount() const
74 {
75 return aElements.size();
76 }
77
GetElementByIndex(size_t nIndex) const78 const OUString* ScDPSaveGroupItem::GetElementByIndex(size_t nIndex) const
79 {
80 return (nIndex < aElements.size()) ? &aElements[ nIndex ] : nullptr;
81 }
82
Rename(const OUString & rNewName)83 void ScDPSaveGroupItem::Rename( const OUString& rNewName )
84 {
85 aGroupName = rNewName;
86 }
87
RemoveElementsFromGroups(ScDPSaveGroupDimension & rDimension) const88 void ScDPSaveGroupItem::RemoveElementsFromGroups( ScDPSaveGroupDimension& rDimension ) const
89 {
90 // remove this group's elements from their groups in rDimension
91 // (rDimension must be a different dimension from the one which contains this)
92
93 for ( const auto& rElement : aElements )
94 rDimension.RemoveFromGroups( rElement );
95 }
96
ConvertElementsToItems(SvNumberFormatter * pFormatter) const97 void ScDPSaveGroupItem::ConvertElementsToItems(SvNumberFormatter* pFormatter) const
98 {
99 maItems.reserve(aElements.size());
100 for (const auto& rElement : aElements)
101 {
102 sal_uInt32 nFormat = 0;
103 double fValue;
104 ScDPItemData aData;
105 if (pFormatter->IsNumberFormat(rElement, nFormat, fValue))
106 aData.SetValue(fValue);
107 else
108 aData.SetString(rElement);
109
110 maItems.push_back(aData);
111 }
112 }
113
HasInGroup(const ScDPItemData & rItem) const114 bool ScDPSaveGroupItem::HasInGroup(const ScDPItemData& rItem) const
115 {
116 return std::find(maItems.begin(), maItems.end(), rItem) != maItems.end();
117 }
118
AddToData(ScDPGroupDimension & rDataDim) const119 void ScDPSaveGroupItem::AddToData(ScDPGroupDimension& rDataDim) const
120 {
121 ScDPGroupItem aGroup(aGroupName);
122 for (const auto& rItem : maItems)
123 aGroup.AddElement(rItem);
124
125 rDataDim.AddItem(aGroup);
126 }
127
ScDPSaveGroupDimension(const OUString & rSource,const OUString & rName)128 ScDPSaveGroupDimension::ScDPSaveGroupDimension( const OUString& rSource, const OUString& rName ) :
129 aSourceDim( rSource ),
130 aGroupDimName( rName ),
131 nDatePart( 0 )
132 {
133 }
134
ScDPSaveGroupDimension(const OUString & rSource,const OUString & rName,const ScDPNumGroupInfo & rDateInfo,sal_Int32 nPart)135 ScDPSaveGroupDimension::ScDPSaveGroupDimension( const OUString& rSource, const OUString& rName, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nPart ) :
136 aSourceDim( rSource ),
137 aGroupDimName( rName ),
138 aDateInfo( rDateInfo ),
139 nDatePart( nPart )
140 {
141 }
142
SetDateInfo(const ScDPNumGroupInfo & rInfo,sal_Int32 nPart)143 void ScDPSaveGroupDimension::SetDateInfo( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart )
144 {
145 aDateInfo = rInfo;
146 nDatePart = nPart;
147 }
148
AddGroupItem(const ScDPSaveGroupItem & rItem)149 void ScDPSaveGroupDimension::AddGroupItem( const ScDPSaveGroupItem& rItem )
150 {
151 aGroups.push_back( rItem );
152 }
153
CreateGroupName(std::u16string_view rPrefix)154 OUString ScDPSaveGroupDimension::CreateGroupName(std::u16string_view rPrefix)
155 {
156 // create a name for a new group, using "Group1", "Group2" etc. (translated prefix in rPrefix)
157
158 //TODO: look in all dimensions, to avoid clashes with automatic groups (=name of base element)?
159 //TODO: (only dimensions for the same base)
160
161 sal_Int32 nAdd = 1; // first try is "Group1"
162 const sal_Int32 nMaxAdd = nAdd + aGroups.size(); // limit the loop
163 while ( nAdd <= nMaxAdd )
164 {
165 OUString aGroupName = rPrefix + OUString::number( nAdd );
166
167 // look for existing groups
168 bool bExists = std::any_of(aGroups.begin(), aGroups.end(),
169 [&aGroupName](const ScDPSaveGroupItem& rGroup) {
170 return rGroup.GetGroupName() == aGroupName; //TODO: ignore case
171 });
172
173 if ( !bExists )
174 return aGroupName; // found a new name
175
176 ++nAdd; // continue with higher number
177 }
178
179 OSL_FAIL("CreateGroupName: no valid name found");
180 return OUString();
181 }
182
GetNamedGroup(const OUString & rGroupName) const183 const ScDPSaveGroupItem* ScDPSaveGroupDimension::GetNamedGroup( const OUString& rGroupName ) const
184 {
185 return const_cast< ScDPSaveGroupDimension* >( this )->GetNamedGroupAcc( rGroupName );
186 }
187
GetNamedGroupAcc(const OUString & rGroupName)188 ScDPSaveGroupItem* ScDPSaveGroupDimension::GetNamedGroupAcc( const OUString& rGroupName )
189 {
190 auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
191 [&rGroupName](const ScDPSaveGroupItem& rGroup) {
192 return rGroup.GetGroupName() == rGroupName; //TODO: ignore case
193 });
194 if (aIter != aGroups.end())
195 return &*aIter;
196
197 return nullptr; // none found
198 }
199
GetGroupCount() const200 tools::Long ScDPSaveGroupDimension::GetGroupCount() const
201 {
202 return aGroups.size();
203 }
204
GetGroupByIndex(tools::Long nIndex) const205 const ScDPSaveGroupItem& ScDPSaveGroupDimension::GetGroupByIndex( tools::Long nIndex ) const
206 {
207 return aGroups[nIndex];
208 }
209
210
RemoveFromGroups(const OUString & rItemName)211 void ScDPSaveGroupDimension::RemoveFromGroups( const OUString& rItemName )
212 {
213 // if the item is in any group, remove it from the group,
214 // also remove the group if it is empty afterwards
215
216 for ( ScDPSaveGroupItemVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter )
217 if ( aIter->RemoveElement( rItemName ) )
218 {
219 if ( aIter->IsEmpty() ) // removed last item from the group?
220 aGroups.erase( aIter ); // then remove the group
221
222 return; // don't have to look further
223 }
224 }
225
RemoveGroup(const OUString & rGroupName)226 void ScDPSaveGroupDimension::RemoveGroup(const OUString& rGroupName)
227 {
228 auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
229 [&rGroupName](const ScDPSaveGroupItem& rGroup) {
230 return rGroup.GetGroupName() == rGroupName; //TODO: ignore case
231 });
232 if (aIter != aGroups.end())
233 aGroups.erase( aIter );
234 }
235
IsEmpty() const236 bool ScDPSaveGroupDimension::IsEmpty() const
237 {
238 return aGroups.empty();
239 }
240
HasOnlyHidden(const ScDPUniqueStringSet & rVisible)241 bool ScDPSaveGroupDimension::HasOnlyHidden(const ScDPUniqueStringSet& rVisible)
242 {
243 // check if there are only groups that don't appear in the list of visible names
244
245 return std::none_of(aGroups.begin(), aGroups.end(),
246 [&rVisible](const ScDPSaveGroupItem& rGroup) { return rVisible.count(rGroup.GetGroupName()) > 0; });
247 }
248
Rename(const OUString & rNewName)249 void ScDPSaveGroupDimension::Rename( const OUString& rNewName )
250 {
251 aGroupDimName = rNewName;
252 }
253
IsInGroup(const ScDPItemData & rItem) const254 bool ScDPSaveGroupDimension::IsInGroup(const ScDPItemData& rItem) const
255 {
256 return std::any_of(aGroups.begin(), aGroups.end(),
257 [&rItem](const ScDPSaveGroupItem& rGroup) { return rGroup.HasInGroup(rItem); });
258 }
259
260 namespace {
261
isInteger(double fValue)262 bool isInteger(double fValue)
263 {
264 return rtl::math::approxEqual(fValue, rtl::math::approxFloor(fValue));
265 }
266
fillDateGroupDimension(ScDPCache & rCache,ScDPNumGroupInfo & rDateInfo,tools::Long nSourceDim,tools::Long nGroupDim,sal_Int32 nDatePart,const SvNumberFormatter * pFormatter)267 void fillDateGroupDimension(
268 ScDPCache& rCache, ScDPNumGroupInfo& rDateInfo, tools::Long nSourceDim, tools::Long nGroupDim,
269 sal_Int32 nDatePart, const SvNumberFormatter* pFormatter)
270 {
271 // Auto min/max is only used for "Years" part, but the loop is always
272 // needed.
273 double fSourceMin = 0.0;
274 double fSourceMax = 0.0;
275 bool bFirst = true;
276
277 const ScDPCache::ScDPItemDataVec& rItems = rCache.GetDimMemberValues(nSourceDim);
278 for (const ScDPItemData& rItem : rItems)
279 {
280 if (rItem.GetType() != ScDPItemData::Value)
281 continue;
282
283 double fVal = rItem.GetValue();
284 if (bFirst)
285 {
286 fSourceMin = fSourceMax = fVal;
287 bFirst = false;
288 }
289 else
290 {
291 if (fVal < fSourceMin)
292 fSourceMin = fVal;
293 if ( fVal > fSourceMax )
294 fSourceMax = fVal;
295 }
296 }
297
298 // For the start/end values, use the same date rounding as in
299 // ScDPNumGroupDimension::GetNumEntries (but not for the list of
300 // available years).
301 if (rDateInfo.mbAutoStart)
302 rDateInfo.mfStart = rtl::math::approxFloor(fSourceMin);
303 if (rDateInfo.mbAutoEnd)
304 rDateInfo.mfEnd = rtl::math::approxFloor(fSourceMax) + 1;
305
306 //TODO: if not automatic, limit fSourceMin/fSourceMax for list of year values?
307
308 tools::Long nStart = 0, nEnd = 0; // end is inclusive
309
310 switch (nDatePart)
311 {
312 case sheet::DataPilotFieldGroupBy::YEARS:
313 nStart = ScDPUtil::getDatePartValue(
314 fSourceMin, nullptr, sheet::DataPilotFieldGroupBy::YEARS, pFormatter);
315 nEnd = ScDPUtil::getDatePartValue(fSourceMax, nullptr, sheet::DataPilotFieldGroupBy::YEARS, pFormatter);
316 break;
317 case sheet::DataPilotFieldGroupBy::QUARTERS: nStart = 1; nEnd = 4; break;
318 case sheet::DataPilotFieldGroupBy::MONTHS: nStart = 1; nEnd = 12; break;
319 case sheet::DataPilotFieldGroupBy::DAYS: nStart = 1; nEnd = 366; break;
320 case sheet::DataPilotFieldGroupBy::HOURS: nStart = 0; nEnd = 23; break;
321 case sheet::DataPilotFieldGroupBy::MINUTES: nStart = 0; nEnd = 59; break;
322 case sheet::DataPilotFieldGroupBy::SECONDS: nStart = 0; nEnd = 59; break;
323 default:
324 OSL_FAIL("invalid date part");
325 }
326
327 // Now, populate the group items in the cache.
328 rCache.ResetGroupItems(nGroupDim, rDateInfo, nDatePart);
329
330 for (tools::Long nValue = nStart; nValue <= nEnd; ++nValue)
331 rCache.SetGroupItem(nGroupDim, ScDPItemData(nDatePart, nValue));
332
333 // add first/last entry (min/max)
334 rCache.SetGroupItem(nGroupDim, ScDPItemData(nDatePart, ScDPItemData::DateFirst));
335 rCache.SetGroupItem(nGroupDim, ScDPItemData(nDatePart, ScDPItemData::DateLast));
336 }
337
338 }
339
AddToData(ScDPGroupTableData & rData) const340 void ScDPSaveGroupDimension::AddToData( ScDPGroupTableData& rData ) const
341 {
342 tools::Long nSourceIndex = rData.GetDimensionIndex( aSourceDim );
343 if ( nSourceIndex < 0 )
344 return;
345
346 ScDPGroupDimension aDim( nSourceIndex, aGroupDimName );
347 if ( nDatePart )
348 {
349 // date grouping
350
351 aDim.SetDateDimension();
352 }
353 else
354 {
355 // normal (manual) grouping
356
357 for (const auto& rGroup : aGroups)
358 rGroup.AddToData(aDim);
359 }
360
361 rData.AddGroupDimension( aDim );
362 }
363
AddToCache(ScDPCache & rCache) const364 void ScDPSaveGroupDimension::AddToCache(ScDPCache& rCache) const
365 {
366 tools::Long nSourceDim = rCache.GetDimensionIndex(aSourceDim);
367 if (nSourceDim < 0)
368 return;
369
370 tools::Long nDim = rCache.AppendGroupField();
371 SvNumberFormatter* pFormatter = rCache.GetDoc().GetFormatTable();
372
373 if (nDatePart)
374 {
375 fillDateGroupDimension(rCache, aDateInfo, nSourceDim, nDim, nDatePart, pFormatter);
376 return;
377 }
378
379 rCache.ResetGroupItems(nDim, aDateInfo, 0);
380 for (const ScDPSaveGroupItem& rGI : aGroups)
381 {
382 rGI.ConvertElementsToItems(pFormatter);
383 rCache.SetGroupItem(nDim, ScDPItemData(rGI.GetGroupName()));
384 }
385
386 const ScDPCache::ScDPItemDataVec& rItems = rCache.GetDimMemberValues(nSourceDim);
387 for (const ScDPItemData& rItem : rItems)
388 {
389 if (!IsInGroup(rItem))
390 // Not in any group. Add as its own group.
391 rCache.SetGroupItem(nDim, rItem);
392 }
393 }
394
ScDPSaveNumGroupDimension(const OUString & rName,const ScDPNumGroupInfo & rInfo)395 ScDPSaveNumGroupDimension::ScDPSaveNumGroupDimension( const OUString& rName, const ScDPNumGroupInfo& rInfo ) :
396 aDimensionName( rName ),
397 aGroupInfo( rInfo ),
398 nDatePart( 0 )
399 {
400 }
401
ScDPSaveNumGroupDimension(const OUString & rName,const ScDPNumGroupInfo & rDateInfo,sal_Int32 nPart)402 ScDPSaveNumGroupDimension::ScDPSaveNumGroupDimension( const OUString& rName, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nPart ) :
403 aDimensionName( rName ),
404 aDateInfo( rDateInfo ),
405 nDatePart( nPart )
406 {
407 }
408
AddToData(ScDPGroupTableData & rData) const409 void ScDPSaveNumGroupDimension::AddToData( ScDPGroupTableData& rData ) const
410 {
411 tools::Long nSource = rData.GetDimensionIndex( aDimensionName );
412 if ( nSource >= 0 )
413 {
414 ScDPNumGroupDimension aDim( aGroupInfo ); // aGroupInfo: value grouping
415 if ( nDatePart )
416 aDim.SetDateDimension();
417
418 rData.SetNumGroupDimension( nSource, aDim );
419 }
420 }
421
AddToCache(ScDPCache & rCache) const422 void ScDPSaveNumGroupDimension::AddToCache(ScDPCache& rCache) const
423 {
424 tools::Long nDim = rCache.GetDimensionIndex(aDimensionName);
425 if (nDim < 0)
426 return;
427
428 if (aDateInfo.mbEnable)
429 {
430 // Date grouping
431 SvNumberFormatter* pFormatter = rCache.GetDoc().GetFormatTable();
432 fillDateGroupDimension(rCache, aDateInfo, nDim, nDim, nDatePart, pFormatter);
433 }
434 else if (aGroupInfo.mbEnable)
435 {
436 // Number-range grouping
437
438 // Look through the source entries for non-integer numbers, minimum
439 // and maximum.
440
441 // non-integer GroupInfo values count, too
442 aGroupInfo.mbIntegerOnly =
443 (aGroupInfo.mbAutoStart || isInteger(aGroupInfo.mfStart)) &&
444 (aGroupInfo.mbAutoEnd || isInteger(aGroupInfo.mfEnd)) &&
445 isInteger(aGroupInfo.mfStep);
446
447 double fSourceMin = 0.0;
448 double fSourceMax = 0.0;
449 bool bFirst = true;
450
451 const ScDPCache::ScDPItemDataVec& rItems = rCache.GetDimMemberValues(nDim);
452 for (const ScDPItemData& rItem : rItems)
453 {
454 if (rItem.GetType() != ScDPItemData::Value)
455 continue;
456
457 double fValue = rItem.GetValue();
458 if (bFirst)
459 {
460 fSourceMin = fSourceMax = fValue;
461 bFirst = false;
462 continue;
463 }
464
465 if (fValue < fSourceMin)
466 fSourceMin = fValue;
467 if (fValue > fSourceMax)
468 fSourceMax = fValue;
469
470 if (aGroupInfo.mbIntegerOnly && !isInteger(fValue))
471 {
472 // If any non-integer numbers are involved, the group labels
473 // are shown including their upper limit.
474 aGroupInfo.mbIntegerOnly = false;
475 }
476 }
477
478 if (aGroupInfo.mbDateValues)
479 {
480 // special handling for dates: always integer, round down limits
481 aGroupInfo.mbIntegerOnly = true;
482 fSourceMin = rtl::math::approxFloor(fSourceMin);
483 fSourceMax = rtl::math::approxFloor(fSourceMax) + 1;
484 }
485
486 if (aGroupInfo.mbAutoStart)
487 aGroupInfo.mfStart = fSourceMin;
488 if (aGroupInfo.mbAutoEnd)
489 aGroupInfo.mfEnd = fSourceMax;
490
491 //TODO: limit number of entries?
492
493 tools::Long nLoopCount = 0;
494 double fLoop = aGroupInfo.mfStart;
495
496 rCache.ResetGroupItems(nDim, aGroupInfo, 0);
497
498 // Use "less than" instead of "less or equal" for the loop - don't
499 // create a group that consists only of the end value. Instead, the
500 // end value is then included in the last group (last group is bigger
501 // than the others). The first group has to be created nonetheless.
502 // GetNumGroupForValue has corresponding logic.
503
504 bool bFirstGroup = true;
505 while (bFirstGroup || (fLoop < aGroupInfo.mfEnd && !rtl::math::approxEqual(fLoop, aGroupInfo.mfEnd)))
506 {
507 ScDPItemData aItem;
508 aItem.SetRangeStart(fLoop);
509 rCache.SetGroupItem(nDim, aItem);
510 ++nLoopCount;
511 fLoop = aGroupInfo.mfStart + nLoopCount * aGroupInfo.mfStep;
512 bFirstGroup = false;
513
514 // ScDPItemData values are compared with approxEqual
515 }
516
517 ScDPItemData aItem;
518 aItem.SetRangeFirst();
519 rCache.SetGroupItem(nDim, aItem);
520
521 aItem.SetRangeLast();
522 rCache.SetGroupItem(nDim, aItem);
523 }
524 }
525
SetGroupInfo(const ScDPNumGroupInfo & rNew)526 void ScDPSaveNumGroupDimension::SetGroupInfo( const ScDPNumGroupInfo& rNew )
527 {
528 aGroupInfo = rNew;
529 }
530
SetDateInfo(const ScDPNumGroupInfo & rInfo,sal_Int32 nPart)531 void ScDPSaveNumGroupDimension::SetDateInfo( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart )
532 {
533 aDateInfo = rInfo;
534 nDatePart = nPart;
535 }
536
537 namespace {
538
539 struct ScDPSaveGroupDimNameFunc
540 {
541 OUString maDimName;
ScDPSaveGroupDimNameFunc__anonde2870710711::ScDPSaveGroupDimNameFunc542 explicit ScDPSaveGroupDimNameFunc( const OUString& rDimName ) : maDimName( rDimName ) {}
operator ()__anonde2870710711::ScDPSaveGroupDimNameFunc543 bool operator()( const ScDPSaveGroupDimension& rGroupDim ) const { return rGroupDim.GetGroupDimName() == maDimName; }
544 };
545
546 struct ScDPSaveGroupSourceNameFunc
547 {
548 OUString maSrcDimName;
ScDPSaveGroupSourceNameFunc__anonde2870710711::ScDPSaveGroupSourceNameFunc549 explicit ScDPSaveGroupSourceNameFunc( const OUString& rSrcDimName ) : maSrcDimName( rSrcDimName ) {}
operator ()__anonde2870710711::ScDPSaveGroupSourceNameFunc550 bool operator()( const ScDPSaveGroupDimension& rGroupDim ) const { return rGroupDim.GetSourceDimName() == maSrcDimName; }
551 };
552
553 } // namespace
554
ScDPDimensionSaveData()555 ScDPDimensionSaveData::ScDPDimensionSaveData()
556 {
557 }
558
operator ==(const ScDPDimensionSaveData &) const559 bool ScDPDimensionSaveData::operator==( const ScDPDimensionSaveData& ) const
560 {
561 return false;
562 }
563
AddGroupDimension(const ScDPSaveGroupDimension & rGroupDim)564 void ScDPDimensionSaveData::AddGroupDimension( const ScDPSaveGroupDimension& rGroupDim )
565 {
566 OSL_ENSURE( ::std::none_of( maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDim.GetGroupDimName() ) ),
567 "ScDPDimensionSaveData::AddGroupDimension - group dimension exists already" );
568 // ReplaceGroupDimension() adds new or replaces existing
569 ReplaceGroupDimension( rGroupDim );
570 }
571
ReplaceGroupDimension(const ScDPSaveGroupDimension & rGroupDim)572 void ScDPDimensionSaveData::ReplaceGroupDimension( const ScDPSaveGroupDimension& rGroupDim )
573 {
574 ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
575 maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDim.GetGroupDimName() ) );
576 if( aIt == maGroupDims.end() )
577 maGroupDims.push_back( rGroupDim );
578 else
579 *aIt = rGroupDim;
580 }
581
RemoveGroupDimension(const OUString & rGroupDimName)582 void ScDPDimensionSaveData::RemoveGroupDimension( const OUString& rGroupDimName )
583 {
584 ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
585 maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDimName ) );
586 if( aIt != maGroupDims.end() )
587 maGroupDims.erase( aIt );
588 }
589
AddNumGroupDimension(const ScDPSaveNumGroupDimension & rGroupDim)590 void ScDPDimensionSaveData::AddNumGroupDimension( const ScDPSaveNumGroupDimension& rGroupDim )
591 {
592 OSL_ENSURE( maNumGroupDims.count( rGroupDim.GetDimensionName() ) == 0,
593 "ScDPDimensionSaveData::AddNumGroupDimension - numeric group dimension exists already" );
594 // ReplaceNumGroupDimension() adds new or replaces existing
595 ReplaceNumGroupDimension( rGroupDim );
596 }
597
ReplaceNumGroupDimension(const ScDPSaveNumGroupDimension & rGroupDim)598 void ScDPDimensionSaveData::ReplaceNumGroupDimension( const ScDPSaveNumGroupDimension& rGroupDim )
599 {
600 ScDPSaveNumGroupDimMap::iterator aIt = maNumGroupDims.find( rGroupDim.GetDimensionName() );
601 if( aIt == maNumGroupDims.end() )
602 maNumGroupDims.emplace( rGroupDim.GetDimensionName(), rGroupDim );
603 else
604 aIt->second = rGroupDim;
605 }
606
RemoveNumGroupDimension(const OUString & rGroupDimName)607 void ScDPDimensionSaveData::RemoveNumGroupDimension( const OUString& rGroupDimName )
608 {
609 maNumGroupDims.erase( rGroupDimName );
610 }
611
WriteToData(ScDPGroupTableData & rData) const612 void ScDPDimensionSaveData::WriteToData( ScDPGroupTableData& rData ) const
613 {
614 // rData is assumed to be empty
615 // AddToData also handles date grouping
616
617 for( const auto& rGroupDim : maGroupDims )
618 rGroupDim.AddToData( rData );
619
620 for( const auto& rEntry : maNumGroupDims )
621 rEntry.second.AddToData( rData );
622 }
623
WriteToCache(ScDPCache & rCache) const624 void ScDPDimensionSaveData::WriteToCache(ScDPCache& rCache) const
625 {
626 for (const auto& rEntry : maGroupDims)
627 rEntry.AddToCache(rCache);
628 for (const auto& rEntry : maNumGroupDims)
629 rEntry.second.AddToCache(rCache);
630 }
631
GetGroupDimForBase(const OUString & rBaseDimName) const632 const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetGroupDimForBase( const OUString& rBaseDimName ) const
633 {
634 return const_cast< ScDPDimensionSaveData* >( this )->GetGroupDimAccForBase( rBaseDimName );
635 }
636
GetNamedGroupDim(const OUString & rGroupDimName) const637 const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNamedGroupDim( const OUString& rGroupDimName ) const
638 {
639 return const_cast< ScDPDimensionSaveData* >( this )->GetNamedGroupDimAcc( rGroupDimName );
640 }
641
GetFirstNamedGroupDim(const OUString & rBaseDimName) const642 const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetFirstNamedGroupDim( const OUString& rBaseDimName ) const
643 {
644 return const_cast< ScDPDimensionSaveData* >( this )->GetFirstNamedGroupDimAcc( rBaseDimName );
645 }
646
GetNextNamedGroupDim(const OUString & rGroupDimName) const647 const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNextNamedGroupDim( const OUString& rGroupDimName ) const
648 {
649 return const_cast< ScDPDimensionSaveData* >( this )->GetNextNamedGroupDimAcc( rGroupDimName );
650 }
651
GetNumGroupDim(const OUString & rGroupDimName) const652 const ScDPSaveNumGroupDimension* ScDPDimensionSaveData::GetNumGroupDim( const OUString& rGroupDimName ) const
653 {
654 return const_cast< ScDPDimensionSaveData* >( this )->GetNumGroupDimAcc( rGroupDimName );
655 }
656
GetGroupDimAccForBase(const OUString & rBaseDimName)657 ScDPSaveGroupDimension* ScDPDimensionSaveData::GetGroupDimAccForBase( const OUString& rBaseDimName )
658 {
659 ScDPSaveGroupDimension* pGroupDim = GetFirstNamedGroupDimAcc( rBaseDimName );
660 return pGroupDim ? pGroupDim : GetNextNamedGroupDimAcc( rBaseDimName );
661 }
662
GetNamedGroupDimAcc(const OUString & rGroupDimName)663 ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNamedGroupDimAcc( const OUString& rGroupDimName )
664 {
665 ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
666 maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDimName ) );
667 return (aIt == maGroupDims.end()) ? nullptr : &*aIt;
668 }
669
GetFirstNamedGroupDimAcc(const OUString & rBaseDimName)670 ScDPSaveGroupDimension* ScDPDimensionSaveData::GetFirstNamedGroupDimAcc( const OUString& rBaseDimName )
671 {
672 ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
673 maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupSourceNameFunc( rBaseDimName ) );
674 return (aIt == maGroupDims.end()) ? nullptr : &*aIt;
675 }
676
GetNextNamedGroupDimAcc(const OUString & rGroupDimName)677 ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNextNamedGroupDimAcc( const OUString& rGroupDimName )
678 {
679 // find the group dimension with the passed name
680 ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
681 maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDimName ) );
682 // find next group dimension based on the same source dimension name
683 if( aIt != maGroupDims.end() )
684 aIt = ::std::find_if( aIt + 1, maGroupDims.end(), ScDPSaveGroupSourceNameFunc( aIt->GetSourceDimName() ) );
685 return (aIt == maGroupDims.end()) ? nullptr : &*aIt;
686 }
687
GetNumGroupDimAcc(const OUString & rGroupDimName)688 ScDPSaveNumGroupDimension* ScDPDimensionSaveData::GetNumGroupDimAcc( const OUString& rGroupDimName )
689 {
690 ScDPSaveNumGroupDimMap::iterator aIt = maNumGroupDims.find( rGroupDimName );
691 return (aIt == maNumGroupDims.end()) ? nullptr : &aIt->second;
692 }
693
HasGroupDimensions() const694 bool ScDPDimensionSaveData::HasGroupDimensions() const
695 {
696 return !maGroupDims.empty() || !maNumGroupDims.empty();
697 }
698
CollectDateParts(const OUString & rBaseDimName) const699 sal_Int32 ScDPDimensionSaveData::CollectDateParts( const OUString& rBaseDimName ) const
700 {
701 sal_Int32 nParts = 0;
702 // start with part of numeric group
703 if( const ScDPSaveNumGroupDimension* pNumDim = GetNumGroupDim( rBaseDimName ) )
704 nParts |= pNumDim->GetDatePart();
705 // collect parts from all matching group dimensions
706 for( const ScDPSaveGroupDimension* pGroupDim = GetFirstNamedGroupDim( rBaseDimName ); pGroupDim; pGroupDim = GetNextNamedGroupDim( pGroupDim->GetGroupDimName() ) )
707 nParts |= pGroupDim->GetDatePart();
708
709 return nParts;
710 }
711
CreateGroupDimName(const OUString & rSourceName,const ScDPObject & rObject,bool bAllowSource,const std::vector<OUString> * pDeletedNames)712 OUString ScDPDimensionSaveData::CreateGroupDimName(
713 const OUString& rSourceName, const ScDPObject& rObject, bool bAllowSource,
714 const std::vector<OUString>* pDeletedNames )
715 {
716 // create a name for the new dimension by appending a number to the original
717 // dimension's name
718
719 bool bUseSource = bAllowSource; // if set, try the unchanged original name first
720
721 sal_Int32 nAdd = 2; // first try is "Name2"
722 const sal_Int32 nMaxAdd = 1000; // limit the loop
723 while ( nAdd <= nMaxAdd )
724 {
725 OUString aDimName( rSourceName );
726 if ( !bUseSource )
727 aDimName += OUString::number(nAdd);
728
729 // look for existing group dimensions
730 bool bExists = std::any_of(maGroupDims.begin(), maGroupDims.end(),
731 [&aDimName](const ScDPSaveGroupDimension& rDim) {
732 return rDim.GetGroupDimName() == aDimName; //TODO: ignore case
733 });
734
735 // look for base dimensions that happen to have that name
736 if ( !bExists && rObject.IsDimNameInUse( aDimName ) )
737 {
738 if ( pDeletedNames &&
739 std::find( pDeletedNames->begin(), pDeletedNames->end(), aDimName ) != pDeletedNames->end() )
740 {
741 // allow the name anyway if the name is in pDeletedNames
742 }
743 else
744 bExists = true;
745 }
746
747 if ( !bExists )
748 return aDimName; // found a new name
749
750 if ( bUseSource )
751 bUseSource = false;
752 else
753 ++nAdd; // continue with higher number
754 }
755 OSL_FAIL("CreateGroupDimName: no valid name found");
756 return OUString();
757 }
758
759 namespace
760 {
761 const char* aDatePartIds[] =
762 {
763 STR_DPFIELD_GROUP_BY_SECONDS,
764 STR_DPFIELD_GROUP_BY_MINUTES,
765 STR_DPFIELD_GROUP_BY_HOURS,
766 STR_DPFIELD_GROUP_BY_DAYS,
767 STR_DPFIELD_GROUP_BY_MONTHS,
768 STR_DPFIELD_GROUP_BY_QUARTERS,
769 STR_DPFIELD_GROUP_BY_YEARS
770 };
771 }
772
CreateDateGroupDimName(sal_Int32 nDatePart,const ScDPObject & rObject,bool bAllowSource,const std::vector<OUString> * pDeletedNames)773 OUString ScDPDimensionSaveData::CreateDateGroupDimName(
774 sal_Int32 nDatePart, const ScDPObject& rObject, bool bAllowSource,
775 const std::vector<OUString>* pDeletedNames )
776 {
777 using namespace css::sheet::DataPilotFieldGroupBy;
778 OUString aPartName;
779 switch( nDatePart )
780 {
781 case SECONDS: aPartName = ScResId(aDatePartIds[0]); break;
782 case MINUTES: aPartName = ScResId(aDatePartIds[1]); break;
783 case HOURS: aPartName = ScResId(aDatePartIds[2]); break;
784 case DAYS: aPartName = ScResId(aDatePartIds[3]); break;
785 case MONTHS: aPartName = ScResId(aDatePartIds[4]); break;
786 case QUARTERS: aPartName = ScResId(aDatePartIds[5]); break;
787 case YEARS: aPartName = ScResId(aDatePartIds[6]); break;
788 }
789 OSL_ENSURE(!aPartName.isEmpty(), "ScDPDimensionSaveData::CreateDateGroupDimName - invalid date part");
790 return CreateGroupDimName( aPartName, rObject, bAllowSource, pDeletedNames );
791 }
792
793 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
794