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