blob: 2566a67ca3c03c718b0884fcab77c778a9c07716 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
// INCLUDE ---------------------------------------------------------------
#include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
#include <tools/debug.hxx>
#include <rtl/math.hxx>
#include <unotools/localedatawrapper.hxx>
#include <svl/zforlist.hxx>
#include "dpgroup.hxx"
#include "collect.hxx"
#include "global.hxx"
#include "document.hxx"
#include "dpcachetable.hxx"
#include "dptabsrc.hxx"
#include "dptabres.hxx"
#include "dpobject.hxx"
#include "dpglobal.hxx"
#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
#include <vector>
#include <hash_set>
#include <hash_map>
using namespace ::com::sun::star;
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::UNO_QUERY_THROW;
using ::rtl::OUString;
using ::rtl::OUStringHash;
using ::std::vector;
using ::std::hash_set;
using ::std::hash_map;
using ::boost::shared_ptr;
#define D_TIMEFACTOR 86400.0
const sal_uInt16 SC_DP_LEAPYEAR = 1648; // arbitrary leap year for date calculations
// part values for the extra "<" and ">" entries (same for all parts)
const sal_Int32 SC_DP_DATE_FIRST = -1;
const sal_Int32 SC_DP_DATE_LAST = 10000;
// ============================================================================
namespace
{
sal_Bool lcl_Search( SCCOL nSourceDim, ScDPTableDataCache* pCache , const std::vector< SCROW >& vIdx, SCROW nNew , SCROW& rIndex)
{
rIndex = vIdx.size();
sal_Bool bFound = sal_False;
SCROW nLo = 0;
SCROW nHi = vIdx.size() - 1;
SCROW nIndex;
long nCompare;
while (nLo <= nHi)
{
nIndex = (nLo + nHi) / 2;
const ScDPItemData* pData = pCache->GetItemDataById( nSourceDim, vIdx[nIndex] );
const ScDPItemData* pDataInsert = pCache->GetItemDataById( nSourceDim, nNew );
nCompare = ScDPItemData::Compare( *pData, *pDataInsert );
if (nCompare < 0)
nLo = nIndex + 1;
else
{
nHi = nIndex - 1;
if (nCompare == 0)
{
bFound = sal_True;
nLo = nIndex;
}
}
}
rIndex = nLo;
return bFound;
}
void lcl_Insert( SCCOL nSourceDim, ScDPTableDataCache* pCache , std::vector< SCROW >& vIdx, SCROW nNew )
{
SCROW nIndex = 0;
if ( !lcl_Search( nSourceDim, pCache, vIdx, nNew ,nIndex ) )
vIdx.insert( vIdx.begin()+nIndex, nNew );
}
template<bool bUpdateData>
SCROW lcl_InsertValue( SCCOL nSourceDim, ScDPTableDataCache* pCache , std::vector< SCROW >& vIdx, const ScDPItemData & rData );
template<>
SCROW lcl_InsertValue<false>( SCCOL nSourceDim, ScDPTableDataCache* pCache , std::vector< SCROW >& vIdx, const ScDPItemData & rData )
{
SCROW nNewID = pCache->GetAdditionalItemID( rData );
lcl_Insert( nSourceDim, pCache, vIdx, nNewID );
return nNewID;
}
template<>
SCROW lcl_InsertValue<true>( SCCOL nSourceDim, ScDPTableDataCache* pCache , std::vector< SCROW >& vIdx, const ScDPItemData & rData )
{
SCROW nItemId = lcl_InsertValue<false>( nSourceDim, pCache, vIdx, rData );
if( const ScDPItemData *pData = pCache->GetItemDataById( nSourceDim, nItemId ) )
const_cast<ScDPItemData&>(*pData) = rData;
return nItemId;
}
template<bool bUpdateData>
void lcl_InsertValue ( SCCOL nSourceDim, ScDPTableDataCache* pCache , std::vector< SCROW >& vIdx, const String& rString, const double& fValue )
{
lcl_InsertValue<bUpdateData>( nSourceDim, pCache, vIdx, ScDPItemData( rString, fValue, sal_True ) );
}
template<bool bUpdateData>
void lcl_InsertValue ( SCCOL nSourceDim, ScDPTableDataCache* pCache , std::vector< SCROW >& vIdx, const String& rString, const double& fValue, sal_Int32 nDatePart )
{
lcl_InsertValue<bUpdateData>( nSourceDim, pCache, vIdx, ScDPItemData( nDatePart, rString, fValue, ScDPItemData::MK_DATA|ScDPItemData::MK_VAL|ScDPItemData::MK_DATEPART ) );
}
void lcl_AppendDateStr( rtl::OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter )
{
sal_uLong nFormat = pFormatter->GetStandardFormat( NUMBERFORMAT_DATE, ScGlobal::eLnge );
String aString;
pFormatter->GetInputLineString( fValue, nFormat, aString );
rBuffer.append( aString );
}
String lcl_GetNumGroupName( double fStartValue, const ScDPNumGroupInfo& rInfo,
bool bHasNonInteger, sal_Unicode cDecSeparator, SvNumberFormatter* pFormatter )
{
DBG_ASSERT( cDecSeparator != 0, "cDecSeparator not initialized" );
double fStep = rInfo.Step;
double fEndValue = fStartValue + fStep;
if ( !bHasNonInteger && ( rInfo.DateValues || !rtl::math::approxEqual( fEndValue, rInfo.End ) ) )
{
// The second number of the group label is
// (first number + size - 1) if there are only integer numbers,
// (first number + size) if any non-integer numbers are involved.
// Exception: The last group (containing the end value) is always
// shown as including the end value (but not for dates).
fEndValue -= 1.0;
}
if ( fEndValue > rInfo.End && !rInfo.AutoEnd )
{
// limit the last group to the end value
fEndValue = rInfo.End;
}
rtl::OUStringBuffer aBuffer;
if ( rInfo.DateValues )
{
lcl_AppendDateStr( aBuffer, fStartValue, pFormatter );
aBuffer.appendAscii( " - " ); // with spaces
lcl_AppendDateStr( aBuffer, fEndValue, pFormatter );
}
else
{
rtl::math::doubleToUStringBuffer( aBuffer, fStartValue, rtl_math_StringFormat_Automatic,
rtl_math_DecimalPlaces_Max, cDecSeparator, true );
aBuffer.append( (sal_Unicode) '-' );
rtl::math::doubleToUStringBuffer( aBuffer, fEndValue, rtl_math_StringFormat_Automatic,
rtl_math_DecimalPlaces_Max, cDecSeparator, true );
}
return aBuffer.makeStringAndClear();
}
String lcl_GetSpecialNumGroupName( double fValue, bool bFirst, sal_Unicode cDecSeparator,
bool bDateValues, SvNumberFormatter* pFormatter )
{
DBG_ASSERT( cDecSeparator != 0, "cDecSeparator not initialized" );
rtl::OUStringBuffer aBuffer;
aBuffer.append((sal_Unicode)( bFirst ? '<' : '>' ));
if ( bDateValues )
lcl_AppendDateStr( aBuffer, fValue, pFormatter );
else
rtl::math::doubleToUStringBuffer( aBuffer, fValue, rtl_math_StringFormat_Automatic,
rtl_math_DecimalPlaces_Max, cDecSeparator, true );
return aBuffer.makeStringAndClear();
}
inline bool IsInteger( double fValue )
{
return rtl::math::approxEqual( fValue, rtl::math::approxFloor(fValue) );
}
String lcl_GetNumGroupForValue( double fValue, const ScDPNumGroupInfo& rInfo, bool bHasNonInteger,
sal_Unicode cDecSeparator, double& rGroupValue, ScDocument* pDoc )
{
SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
if ( fValue < rInfo.Start && !rtl::math::approxEqual( fValue, rInfo.Start ) )
{
rGroupValue = rInfo.Start - rInfo.Step;
return lcl_GetSpecialNumGroupName( rInfo.Start, true, cDecSeparator, rInfo.DateValues, pFormatter );
}
if ( fValue > rInfo.End && !rtl::math::approxEqual( fValue, rInfo.End ) )
{
rGroupValue = rInfo.End + rInfo.Step;
return lcl_GetSpecialNumGroupName( rInfo.End, false, cDecSeparator, rInfo.DateValues, pFormatter );
}
double fDiff = fValue - rInfo.Start;
double fDiv = rtl::math::approxFloor( fDiff / rInfo.Step );
double fGroupStart = rInfo.Start + fDiv * rInfo.Step;
if ( rtl::math::approxEqual( fGroupStart, rInfo.End ) &&
!rtl::math::approxEqual( fGroupStart, rInfo.Start ) )
{
if ( !rInfo.DateValues )
{
// A group that would consist only of the end value is not created,
// instead the value is included in the last group before. So the
// previous group is used if the calculated group start value is the
// selected end value.
fDiv -= 1.0;
fGroupStart = rInfo.Start + fDiv * rInfo.Step;
}
else
{
// For date values, the end value is instead treated as above the limit
// if it would be a group of its own.
rGroupValue = rInfo.End + rInfo.Step;
return lcl_GetSpecialNumGroupName( rInfo.End, false, cDecSeparator, rInfo.DateValues, pFormatter );
}
}
rGroupValue = fGroupStart;
return lcl_GetNumGroupName( fGroupStart, rInfo, bHasNonInteger, cDecSeparator, pFormatter );
}
}
class ScDPGroupDateFilter : public ScDPCacheTable::FilterBase
{
public:
ScDPGroupDateFilter(double fMatchValue, sal_Int32 nDatePart,
const Date* pNullDate, const ScDPNumGroupInfo* pNumInfo);
// Wang Xu Ming -- 2009-8-17
// DataPilot Migration - Cache&&Performance
virtual bool match(const ScDPItemData & rCellData) const;
// End Comments
private:
ScDPGroupDateFilter(); // disabled
const Date* mpNullDate;
const ScDPNumGroupInfo* mpNumInfo;
double mfMatchValue;
sal_Int32 mnDatePart;
};
// ----------------------------------------------------------------------------
ScDPGroupDateFilter::ScDPGroupDateFilter(double fMatchValue, sal_Int32 nDatePart,
const Date* pNullDate, const ScDPNumGroupInfo* pNumInfo) :
mpNullDate(pNullDate),
mpNumInfo(pNumInfo),
mfMatchValue(fMatchValue),
mnDatePart(nDatePart)
{
// fprintf(stdout, "ScDPCacheTable:DateGroupFilter::DateGroupFilter: match value = %g; date part = %ld\n",
// mfMatchValue, mnDatePart);
}
bool ScDPGroupDateFilter::match( const ScDPItemData & rCellData ) const
{
using namespace ::com::sun::star::sheet;
using ::rtl::math::approxFloor;
using ::rtl::math::approxEqual;
if ( !rCellData.IsValue() )
return false;
// ScDPCacheCell rCell( rCellData.fValue );
if (!mpNumInfo)
return false;
// Start and end dates are inclusive. (An end date without a time value
// is included, while an end date with a time value is not.)
if ( rCellData.GetValue() < mpNumInfo->Start && !approxEqual(rCellData.GetValue(), mpNumInfo->Start) )
return static_cast<sal_Int32>(mfMatchValue) == SC_DP_DATE_FIRST;
if ( rCellData.GetValue() > mpNumInfo->End && !approxEqual(rCellData.GetValue(), mpNumInfo->End) )
return static_cast<sal_Int32>(mfMatchValue) == SC_DP_DATE_LAST;
if (mnDatePart == DataPilotFieldGroupBy::HOURS || mnDatePart == DataPilotFieldGroupBy::MINUTES ||
mnDatePart == DataPilotFieldGroupBy::SECONDS)
{
// handle time
// (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded)
double time = rCellData.GetValue() - approxFloor(rCellData.GetValue());
long seconds = static_cast<long>(approxFloor(time*D_TIMEFACTOR + 0.5));
switch (mnDatePart)
{
case DataPilotFieldGroupBy::HOURS:
{
sal_Int32 hrs = seconds / 3600;
sal_Int32 matchHrs = static_cast<sal_Int32>(mfMatchValue);
return hrs == matchHrs;
}
case DataPilotFieldGroupBy::MINUTES:
{
sal_Int32 minutes = (seconds % 3600) / 60;
sal_Int32 matchMinutes = static_cast<sal_Int32>(mfMatchValue);
return minutes == matchMinutes;
}
case DataPilotFieldGroupBy::SECONDS:
{
sal_Int32 sec = seconds % 60;
sal_Int32 matchSec = static_cast<sal_Int32>(mfMatchValue);
return sec == matchSec;
}
default:
DBG_ERROR("invalid time part");
}
return false;
}
Date date = *mpNullDate + static_cast<long>(approxFloor(rCellData.GetValue()));
switch (mnDatePart)
{
case DataPilotFieldGroupBy::YEARS:
{
sal_Int32 year = static_cast<sal_Int32>(date.GetYear());
sal_Int32 matchYear = static_cast<sal_Int32>(mfMatchValue);
return year == matchYear;
}
case DataPilotFieldGroupBy::QUARTERS:
{
sal_Int32 qtr = 1 + (static_cast<sal_Int32>(date.GetMonth()) - 1) / 3;
sal_Int32 matchQtr = static_cast<sal_Int32>(mfMatchValue);
return qtr == matchQtr;
}
case DataPilotFieldGroupBy::MONTHS:
{
sal_Int32 month = static_cast<sal_Int32>(date.GetMonth());
sal_Int32 matchMonth = static_cast<sal_Int32>(mfMatchValue);
return month == matchMonth;
}
case DataPilotFieldGroupBy::DAYS:
{
Date yearStart(1, 1, date.GetYear());
sal_Int32 days = (date - yearStart) + 1; // Jan 01 has value 1
if (days >= 60 && !date.IsLeapYear())
{
// This is not a leap year. Adjust the value accordingly.
++days;
}
sal_Int32 matchDays = static_cast<sal_Int32>(mfMatchValue);
return days == matchDays;
}
default:
DBG_ERROR("invalid date part");
}
return false;
}
// -----------------------------------------------------------------------
ScDPDateGroupHelper::ScDPDateGroupHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart ) :
aNumInfo( rInfo ),
nDatePart( nPart )
{
}
ScDPDateGroupHelper::~ScDPDateGroupHelper()
{
}
String lcl_GetTwoDigitString( sal_Int32 nValue )
{
String aRet = String::CreateFromInt32( nValue );
if ( aRet.Len() < 2 )
aRet.Insert( (sal_Unicode)'0', 0 );
return aRet;
}
String lcl_GetDateGroupName( sal_Int32 nDatePart, sal_Int32 nValue, SvNumberFormatter* pFormatter )
{
String aRet;
switch ( nDatePart )
{
case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS:
aRet = String::CreateFromInt32( nValue );
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS:
aRet = ScGlobal::pLocaleData->getQuarterAbbreviation( (sal_Int16)(nValue - 1) ); // nValue is 1-based
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
//! cache getMonths() result?
aRet = ScGlobal::GetCalendar()->getDisplayName(
::com::sun::star::i18n::CalendarDisplayIndex::MONTH,
sal_Int16(nValue-1), 0 ); // 0-based, get short name
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:
{
Date aDate( 1, 1, SC_DP_LEAPYEAR );
aDate += ( nValue - 1 ); // nValue is 1-based
Date aNullDate = *(pFormatter->GetNullDate());
long nDays = aDate - aNullDate;
sal_uLong nFormat = pFormatter->GetFormatIndex( NF_DATE_SYS_DDMMM, ScGlobal::eLnge );
Color* pColor;
pFormatter->GetOutputString( nDays, nFormat, aRet, &pColor );
}
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS:
//! allow am/pm format?
aRet = lcl_GetTwoDigitString( nValue );
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES:
case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS:
aRet = ScGlobal::pLocaleData->getTimeSep();
aRet.Append( lcl_GetTwoDigitString( nValue ) );
break;
default:
DBG_ERROR("invalid date part");
}
return aRet;
}
sal_Int32 lcl_GetDatePartValue( double fValue, sal_Int32 nDatePart, SvNumberFormatter* pFormatter,
const ScDPNumGroupInfo* pNumInfo )
{
// Start and end are inclusive
// (End date without a time value is included, with a time value it's not)
if ( pNumInfo )
{
if ( fValue < pNumInfo->Start && !rtl::math::approxEqual( fValue, pNumInfo->Start ) )
return SC_DP_DATE_FIRST;
if ( fValue > pNumInfo->End && !rtl::math::approxEqual( fValue, pNumInfo->End ) )
return SC_DP_DATE_LAST;
}
sal_Int32 nResult = 0;
if ( nDatePart == com::sun::star::sheet::DataPilotFieldGroupBy::HOURS || nDatePart == com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES || nDatePart == com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS )
{
// handle time
// (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded)
double fTime = fValue - ::rtl::math::approxFloor(fValue);
long nSeconds = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5);
switch ( nDatePart )
{
case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS:
nResult = nSeconds / 3600;
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES:
nResult = ( nSeconds % 3600 ) / 60;
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS:
nResult = nSeconds % 60;
break;
}
}
else
{
Date aDate = *(pFormatter->GetNullDate());
aDate += (long)::rtl::math::approxFloor( fValue );
switch ( nDatePart )
{
case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS:
nResult = aDate.GetYear();
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS:
nResult = 1 + ( aDate.GetMonth() - 1 ) / 3; // 1..4
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
nResult = aDate.GetMonth(); // 1..12
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:
{
Date aYearStart( 1, 1, aDate.GetYear() );
nResult = ( aDate - aYearStart ) + 1; // Jan 01 has value 1
if ( nResult >= 60 && !aDate.IsLeapYear() )
{
// days are counted from 1 to 366 - if not from a leap year, adjust
++nResult;
}
}
break;
default:
DBG_ERROR("invalid date part");
}
}
return nResult;
}
sal_Bool lcl_DateContained( sal_Int32 nGroupPart, const ScDPItemData& rGroupData,
sal_Int32 nBasePart, const ScDPItemData& rBaseData )
{
if ( !rGroupData.IsValue() || !rBaseData.IsValue() )
{
// non-numeric entries involved: only match equal entries
return rGroupData.IsCaseInsEqual( rBaseData );
}
// no approxFloor needed, values were created from integers
// Wang Xu Ming -- 2009-8-17
// DataPilot Migration - Cache&&Performance
sal_Int32 nGroupValue = (sal_Int32) rGroupData.GetValue();
sal_Int32 nBaseValue = (sal_Int32) rBaseData.GetValue();
// End Comments
if ( nBasePart > nGroupPart )
{
// switch, so the base part is the smaller (inner) part
::std::swap( nGroupPart, nBasePart );
::std::swap( nGroupValue, nBaseValue );
}
if ( nGroupValue == SC_DP_DATE_FIRST || nGroupValue == SC_DP_DATE_LAST ||
nBaseValue == SC_DP_DATE_FIRST || nBaseValue == SC_DP_DATE_LAST )
{
// first/last entry matches only itself
return ( nGroupValue == nBaseValue );
}
sal_Bool bContained = sal_True;
switch ( nBasePart ) // inner part
{
case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
// a month is only contained in its quarter
if ( nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS )
{
// months and quarters are both 1-based
bContained = ( nGroupValue - 1 == ( nBaseValue - 1 ) / 3 );
}
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:
// a day is only contained in its quarter or month
if ( nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS || nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS )
{
Date aDate( 1, 1, SC_DP_LEAPYEAR );
aDate += ( nBaseValue - 1 ); // days are 1-based
sal_Int32 nCompare = aDate.GetMonth();
if ( nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS )
nCompare = ( ( nCompare - 1 ) / 3 ) + 1; // get quarter from date
bContained = ( nGroupValue == nCompare );
}
break;
// other parts: everything is contained
}
return bContained;
}
String lcl_GetSpecialDateName( double fValue, bool bFirst, SvNumberFormatter* pFormatter )
{
rtl::OUStringBuffer aBuffer;
aBuffer.append((sal_Unicode)( bFirst ? '<' : '>' ));
lcl_AppendDateStr( aBuffer, fValue, pFormatter );
return aBuffer.makeStringAndClear();
}
void ScDPDateGroupHelper::FillColumnEntries( SCCOL nSourceDim, ScDPTableDataCache* pCache, std::vector< SCROW >& rEntries, const std::vector< SCROW >& rOriginal ) const
{
// auto min/max is only used for "Years" part, but the loop is always needed
double fSourceMin = 0.0;
double fSourceMax = 0.0;
bool bFirst = true;
size_t nOriginalCount = rOriginal.size();
for (size_t nOriginalPos=0; nOriginalPos<nOriginalCount; nOriginalPos++)
{
const ScDPItemData* pItemData = pCache->GetItemDataById( nSourceDim, rOriginal[nOriginalPos] );
if ( pItemData->HasStringData() )
{
// string data: just copy
lcl_Insert( nSourceDim, pCache , rEntries, rOriginal[nOriginalPos] );
}
else
{
double fSourceValue = pItemData->GetValue();
if ( bFirst )
{
fSourceMin = fSourceMax = fSourceValue;
bFirst = false;
}
else
{
if ( fSourceValue < fSourceMin )
fSourceMin = fSourceValue;
if ( fSourceValue > fSourceMax )
fSourceMax = fSourceValue;
}
}
}
// For the start/end values, use the same date rounding as in ScDPNumGroupDimension::GetNumEntries
// (but not for the list of available years):
if ( aNumInfo.AutoStart )
const_cast<ScDPDateGroupHelper*>(this)->aNumInfo.Start = rtl::math::approxFloor( fSourceMin );
if ( aNumInfo.AutoEnd )
const_cast<ScDPDateGroupHelper*>(this)->aNumInfo.End = rtl::math::approxFloor( fSourceMax ) + 1;
//! if not automatic, limit fSourceMin/fSourceMax for list of year values?
SvNumberFormatter* pFormatter = pCache->GetDoc()->GetFormatTable();
long nStart = 0;
long nEnd = 0; // including
switch ( nDatePart )
{
case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS:
nStart = lcl_GetDatePartValue( fSourceMin, com::sun::star::sheet::DataPilotFieldGroupBy::YEARS, pFormatter, NULL );
nEnd = lcl_GetDatePartValue( fSourceMax, com::sun::star::sheet::DataPilotFieldGroupBy::YEARS, pFormatter, NULL );
break;
case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS: nStart = 1; nEnd = 4; break;
case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS: nStart = 1; nEnd = 12; break;
case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS: nStart = 1; nEnd = 366; break;
case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS: nStart = 0; nEnd = 23; break;
case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES: nStart = 0; nEnd = 59; break;
case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS: nStart = 0; nEnd = 59; break;
default:
DBG_ERROR("invalid date part");
}
for ( sal_Int32 nValue = nStart; nValue <= nEnd; nValue++ )
{
String aName = lcl_GetDateGroupName( nDatePart, nValue, pFormatter );
lcl_InsertValue<false>( nSourceDim, pCache, rEntries, aName, nValue, nDatePart );
}
// add first/last entry (min/max)
String aFirstName = lcl_GetSpecialDateName( aNumInfo.Start, true, pFormatter );
lcl_InsertValue<true>( nSourceDim, pCache, rEntries, aFirstName, SC_DP_DATE_FIRST, nDatePart );
String aLastName = lcl_GetSpecialDateName( aNumInfo.End, false, pFormatter );
lcl_InsertValue<true>( nSourceDim, pCache, rEntries, aLastName, SC_DP_DATE_LAST, nDatePart );
}
// -----------------------------------------------------------------------
ScDPGroupItem::ScDPGroupItem( const ScDPItemData& rName ) :
aGroupName( rName )
{
}
ScDPGroupItem::~ScDPGroupItem()
{
}
void ScDPGroupItem::AddElement( const ScDPItemData& rName )
{
aElements.push_back( rName );
}
bool ScDPGroupItem::HasElement( const ScDPItemData& rData ) const
{
for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); aIter++ )
if ( aIter->IsCaseInsEqual( rData ) )
return true;
return false;
}
bool ScDPGroupItem::HasCommonElement( const ScDPGroupItem& rOther ) const
{
for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); aIter++ )
if ( rOther.HasElement( *aIter ) )
return true;
return false;
}
void ScDPGroupItem::FillGroupFilter( ScDPCacheTable::GroupFilter& rFilter ) const
{
ScDPItemDataVec::const_iterator itrEnd = aElements.end();
for (ScDPItemDataVec::const_iterator itr = aElements.begin(); itr != itrEnd; ++itr)
// Wang Xu Ming -- 2009-8-17
// DataPilot Migration - Cache&&Performance
rFilter.addMatchItem(itr->GetString(), itr->GetValue(), itr->IsValue());
// End Comments
}
// -----------------------------------------------------------------------
ScDPGroupDimension::ScDPGroupDimension( long nSource, const String& rNewName ) :
nSourceDim( nSource ),
nGroupDim( -1 ),
aGroupName( rNewName ),
pDateHelper( NULL )/*,
pCollection( NULL )*/
{
}
ScDPGroupDimension::~ScDPGroupDimension()
{
delete pDateHelper;
maMemberEntries.clear();
}
ScDPGroupDimension::ScDPGroupDimension( const ScDPGroupDimension& rOther ) :
nSourceDim( rOther.nSourceDim ),
nGroupDim( rOther.nGroupDim ),
aGroupName( rOther.aGroupName ),
pDateHelper( NULL ),
aItems( rOther.aItems )
{
if ( rOther.pDateHelper )
pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper );
}
ScDPGroupDimension& ScDPGroupDimension::operator=( const ScDPGroupDimension& rOther )
{
nSourceDim = rOther.nSourceDim;
nGroupDim = rOther.nGroupDim;
aGroupName = rOther.aGroupName;
aItems = rOther.aItems;
delete pDateHelper;
if ( rOther.pDateHelper )
pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper );
else
pDateHelper = NULL;
return *this;
}
void ScDPGroupDimension::MakeDateHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart )
{
delete pDateHelper;
pDateHelper = new ScDPDateGroupHelper( rInfo, nPart );
}
void ScDPGroupDimension::AddItem( const ScDPGroupItem& rItem )
{
aItems.push_back( rItem );
}
void ScDPGroupDimension::SetGroupDim( long nDim )
{
nGroupDim = nDim;
}
// Wang Xu Ming -- 2009-9-2
// DataPilot Migration - Cache&&Performance
const std::vector< SCROW >& ScDPGroupDimension::GetColumnEntries( const ScDPCacheTable& rCacheTable, const std::vector< SCROW >& rOriginal ) const
{
if ( maMemberEntries.empty() )
{
if ( pDateHelper )
{
pDateHelper->FillColumnEntries( (SCCOL)GetSourceDim(), rCacheTable.GetCache(), maMemberEntries, rOriginal );
}
else
{
for (size_t i =0; i < rOriginal.size( ); i ++)
{
const ScDPItemData* pItemData = rCacheTable.GetCache()->GetItemDataById( (SCCOL)GetSourceDim(), rOriginal[i] );
if ( !pItemData || !GetGroupForData( *pItemData ) )
{
// not in any group -> add as its own group
maMemberEntries.push_back( rOriginal[i] );
}
}
long nCount = aItems.size();
for (long i=0; i<nCount; i++)
{
SCROW nNew = rCacheTable.GetCache()->GetAdditionalItemID( aItems[i].GetName() );
lcl_Insert ( (SCCOL)GetSourceDim(), rCacheTable.GetCache(), maMemberEntries, nNew );
}
}
}
return maMemberEntries;
}
// End Comments
const ScDPGroupItem* ScDPGroupDimension::GetGroupForData( const ScDPItemData& rData ) const
{
for ( ScDPGroupItemVec::const_iterator aIter(aItems.begin()); aIter != aItems.end(); aIter++ )
if ( aIter->HasElement( rData ) )
return &*aIter;
return NULL;
}
const ScDPGroupItem* ScDPGroupDimension::GetGroupForName( const ScDPItemData& rName ) const
{
for ( ScDPGroupItemVec::const_iterator aIter(aItems.begin()); aIter != aItems.end(); aIter++ )
if ( aIter->GetName().IsCaseInsEqual( rName ) )
return &*aIter;
return NULL;
}
const ScDPGroupItem* ScDPGroupDimension::GetGroupByIndex( size_t nIndex ) const
{
if (nIndex >= aItems.size())
return NULL;
return &aItems[nIndex];
}
void ScDPGroupDimension::DisposeData()
{
maMemberEntries.clear();
}
// -----------------------------------------------------------------------
ScDPNumGroupDimension::ScDPNumGroupDimension() :
pDateHelper( NULL ),
bHasNonInteger( false ),
cDecSeparator( 0 )
{
}
ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupInfo& rInfo ) :
aGroupInfo( rInfo ),
pDateHelper( NULL ),
bHasNonInteger( false ),
cDecSeparator( 0 )
{
}
ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupDimension& rOther ) :
aGroupInfo( rOther.aGroupInfo ),
pDateHelper( NULL ),
bHasNonInteger( false ),
cDecSeparator( 0 )
{
if ( rOther.pDateHelper )
pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper );
}
ScDPNumGroupDimension& ScDPNumGroupDimension::operator=( const ScDPNumGroupDimension& rOther )
{
aGroupInfo = rOther.aGroupInfo;
delete pDateHelper;
if ( rOther.pDateHelper )
pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper );
else
pDateHelper = NULL;
bHasNonInteger = false;
return *this;
}
void ScDPNumGroupDimension::DisposeData()
{
bHasNonInteger = false;
maMemberEntries.clear();
}
ScDPNumGroupDimension::~ScDPNumGroupDimension()
{
delete pDateHelper;
}
void ScDPNumGroupDimension::MakeDateHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart )
{
delete pDateHelper;
pDateHelper = new ScDPDateGroupHelper( rInfo, nPart );
aGroupInfo.Enable = sal_True; //! or query both?
}
const std::vector< SCROW >& ScDPNumGroupDimension::GetNumEntries( SCCOL nSourceDim, ScDPTableDataCache* pCache,
const std::vector< SCROW >& rOriginal ) const
{
if ( maMemberEntries.empty() )
{
SvNumberFormatter* pFormatter = pCache->GetDoc()->GetFormatTable();
if ( pDateHelper )
pDateHelper->FillColumnEntries( nSourceDim, pCache, maMemberEntries,rOriginal );
else
{
// Copy textual entries.
// Also look through the source entries for non-integer numbers, minimum and maximum.
// GetNumEntries (GetColumnEntries) must be called before accessing the groups
// (this in ensured by calling ScDPLevel::GetMembersObject for all column/row/page
// dimensions before iterating over the values).
cDecSeparator = ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0);
// non-integer GroupInfo values count, too
bHasNonInteger = ( !aGroupInfo.AutoStart && !IsInteger( aGroupInfo.Start ) ) ||
( !aGroupInfo.AutoEnd && !IsInteger( aGroupInfo.End ) ) ||
!IsInteger( aGroupInfo.Step );
double fSourceMin = 0.0;
double fSourceMax = 0.0;
bool bFirst = true;
size_t nOriginalCount = rOriginal.size();
for (size_t nOriginalPos=0; nOriginalPos<nOriginalCount; nOriginalPos++)
{
const ScDPItemData* pItemData = pCache->GetItemDataById( nSourceDim , rOriginal[nOriginalPos] );
if ( pItemData && pItemData ->HasStringData() )
{
lcl_Insert( nSourceDim, pCache, maMemberEntries, rOriginal[nOriginalPos] );
}
else
{
double fSourceValue = pItemData->GetValue();
if ( bFirst )
{
fSourceMin = fSourceMax = fSourceValue;
bFirst = false;
}
else
{
if ( fSourceValue < fSourceMin )
fSourceMin = fSourceValue;
if ( fSourceValue > fSourceMax )
fSourceMax = fSourceValue;
}
if ( !bHasNonInteger && !IsInteger( fSourceValue ) )
{
// if any non-integer numbers are involved, the group labels are
// shown including their upper limit
bHasNonInteger = true;
}
}
}
if ( aGroupInfo.DateValues )
{
// special handling for dates: always integer, round down limits
bHasNonInteger = false;
fSourceMin = rtl::math::approxFloor( fSourceMin );
fSourceMax = rtl::math::approxFloor( fSourceMax ) + 1;
}
if ( aGroupInfo.AutoStart )
const_cast<ScDPNumGroupDimension*>(this)->aGroupInfo.Start = fSourceMin;
if ( aGroupInfo.AutoEnd )
const_cast<ScDPNumGroupDimension*>(this)->aGroupInfo.End = fSourceMax;
//! limit number of entries?
long nLoopCount = 0;
double fLoop = aGroupInfo.Start;
// Use "less than" instead of "less or equal" for the loop - don't create a group
// that consists only of the end value. Instead, the end value is then included
// in the last group (last group is bigger than the others).
// The first group has to be created nonetheless. GetNumGroupForValue has corresponding logic.
bool bFirstGroup = true;
while ( bFirstGroup || ( fLoop < aGroupInfo.End && !rtl::math::approxEqual( fLoop, aGroupInfo.End ) ) )
{
String aName = lcl_GetNumGroupName( fLoop, aGroupInfo, bHasNonInteger, cDecSeparator, pFormatter );
// create a numerical entry to ensure proper sorting
// (in FillMemberResults this needs special handling)
lcl_InsertValue<true>( nSourceDim, pCache, maMemberEntries, aName, fLoop );
++nLoopCount;
fLoop = aGroupInfo.Start + nLoopCount * aGroupInfo.Step;
bFirstGroup = false;
// ScDPItemData values are compared with approxEqual
}
String aFirstName = lcl_GetSpecialNumGroupName( aGroupInfo.Start, true, cDecSeparator, aGroupInfo.DateValues, pFormatter );
lcl_InsertValue<true>( nSourceDim, pCache, maMemberEntries, aFirstName, aGroupInfo.Start - aGroupInfo.Step );
String aLastName = lcl_GetSpecialNumGroupName( aGroupInfo.End, false, cDecSeparator, aGroupInfo.DateValues, pFormatter );
lcl_InsertValue<true>( nSourceDim, pCache, maMemberEntries, aLastName, aGroupInfo.End + aGroupInfo.Step );
}
}
return maMemberEntries;
}
ScDPGroupTableData::ScDPGroupTableData( const shared_ptr<ScDPTableData>& pSource, ScDocument* pDocument ) :
ScDPTableData(pDocument, pSource->GetCacheId() ),
pSourceData( pSource ),
pDoc( pDocument )
{
DBG_ASSERT( pSource, "ScDPGroupTableData: pSource can't be NULL" );
CreateCacheTable();
nSourceCount = pSource->GetColumnCount(); // real columns, excluding data layout
pNumGroups = new ScDPNumGroupDimension[nSourceCount];
}
ScDPGroupTableData::~ScDPGroupTableData()
{
delete[] pNumGroups;
}
void ScDPGroupTableData::AddGroupDimension( const ScDPGroupDimension& rGroup )
{
ScDPGroupDimension aNewGroup( rGroup );
aNewGroup.SetGroupDim( GetColumnCount() ); // new dimension will be at the end
aGroups.push_back( aNewGroup );
aGroupNames.insert( OUString(aNewGroup.GetName()) );
}
void ScDPGroupTableData::SetNumGroupDimension( long nIndex, const ScDPNumGroupDimension& rGroup )
{
if ( nIndex < nSourceCount )
{
pNumGroups[nIndex] = rGroup;
// automatic minimum / maximum is handled in GetNumEntries
}
}
long ScDPGroupTableData::GetDimensionIndex( const String& rName )
{
for (long i=0; i<nSourceCount; i++) // nSourceCount excludes data layout
if ( pSourceData->getDimensionName(i) == rName ) //! ignore case?
return i;
return -1; // none
}
long ScDPGroupTableData::GetColumnCount()
{
return nSourceCount + aGroups.size();
}
bool ScDPGroupTableData::IsNumGroupDimension( long nDimension ) const
{
return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().Enable );
}
void ScDPGroupTableData::GetNumGroupInfo( long nDimension, ScDPNumGroupInfo& rInfo,
bool& rNonInteger, sal_Unicode& rDecimal )
{
if ( nDimension < nSourceCount )
{
rInfo = pNumGroups[nDimension].GetInfo();
rNonInteger = pNumGroups[nDimension].HasNonInteger();
rDecimal = pNumGroups[nDimension].GetDecSeparator();
}
}
// Wang Xu Ming - DataPilot migration
long ScDPGroupTableData::GetMembersCount( long nDim )
{
const std::vector< SCROW >& members = GetColumnEntries( nDim );
return members.size();
}
const std::vector< SCROW >& ScDPGroupTableData::GetColumnEntries( long nColumn )
{
if ( nColumn >= nSourceCount )
{
if ( getIsDataLayoutDimension( nColumn) ) // data layout dimension?
nColumn = nSourceCount; // index of data layout in source data
else
{
const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
long nSourceDim = rGroupDim.GetSourceDim();
// collection is cached at pSourceData, GetColumnEntries can be called every time
const std::vector< SCROW >& rOriginal = pSourceData->GetColumnEntries( nSourceDim );
return rGroupDim.GetColumnEntries( GetCacheTable(), rOriginal );
}
}
if ( IsNumGroupDimension( nColumn ) )
{
// dimension number is unchanged for numerical groups
const std::vector< SCROW >& rOriginal = pSourceData->GetColumnEntries( nColumn );
return pNumGroups[nColumn].GetNumEntries( (SCCOL)nColumn, GetCacheTable().GetCache(), rOriginal );
}
return pSourceData->GetColumnEntries( nColumn );
}
const ScDPItemData* ScDPGroupTableData::GetMemberById( long nDim, long nId )
{
if ( nDim >= nSourceCount )
{
if ( getIsDataLayoutDimension( nDim) )
nDim = nSourceCount;
else
{
const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
nDim = rGroupDim.GetSourceDim();
}
}
return pSourceData->GetMemberById( nDim, nId );
}
String ScDPGroupTableData::getDimensionName(long nColumn)
{
if ( nColumn >= nSourceCount )
{
if ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
nColumn = nSourceCount; // index of data layout in source data
else
return aGroups[nColumn - nSourceCount].GetName();
}
return pSourceData->getDimensionName( nColumn );
}
sal_Bool ScDPGroupTableData::getIsDataLayoutDimension(long nColumn)
{
// position of data layout dimension is moved from source data
return ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ); // data layout dimension?
}
sal_Bool ScDPGroupTableData::IsDateDimension(long nDim)
{
if ( nDim >= nSourceCount )
{
if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
nDim = nSourceCount; // index of data layout in source data
else
nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
}
return pSourceData->IsDateDimension( nDim );
}
sal_uLong ScDPGroupTableData::GetNumberFormat(long nDim)
{
if ( nDim >= nSourceCount )
{
if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
nDim = nSourceCount; // index of data layout in source data
else
nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
}
return pSourceData->GetNumberFormat( nDim );
}
void ScDPGroupTableData::DisposeData()
{
for ( ScDPGroupDimensionVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
aIter->DisposeData();
for ( long i=0; i<nSourceCount; i++ )
pNumGroups[i].DisposeData();
pSourceData->DisposeData();
}
void ScDPGroupTableData::SetEmptyFlags( sal_Bool bIgnoreEmptyRows, sal_Bool bRepeatIfEmpty )
{
pSourceData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
}
bool ScDPGroupTableData::IsRepeatIfEmpty()
{
return pSourceData->IsRepeatIfEmpty();
}
void ScDPGroupTableData::CreateCacheTable()
{
pSourceData->CreateCacheTable();
}
void ScDPGroupTableData::ModifyFilterCriteria(vector<ScDPCacheTable::Criterion>& rCriteria)
{
typedef hash_map<long, const ScDPGroupDimension*> GroupFieldMapType;
GroupFieldMapType aGroupFieldIds;
{
ScDPGroupDimensionVec::const_iterator itr = aGroups.begin(), itrEnd = aGroups.end();
for (; itr != itrEnd; ++itr)
aGroupFieldIds.insert( hash_map<long, const ScDPGroupDimension*>::value_type(itr->GetGroupDim(), &(*itr)) );
}
vector<ScDPCacheTable::Criterion> aNewCriteria;
aNewCriteria.reserve(rCriteria.size() + aGroups.size());
// Go through all the filtered field names and process them appropriately.
vector<ScDPCacheTable::Criterion>::const_iterator itrEnd = rCriteria.end();
GroupFieldMapType::const_iterator itrGrpEnd = aGroupFieldIds.end();
for (vector<ScDPCacheTable::Criterion>::const_iterator itr = rCriteria.begin(); itr != itrEnd; ++itr)
{
ScDPCacheTable::SingleFilter* pFilter = dynamic_cast<ScDPCacheTable::SingleFilter*>(itr->mpFilter.get());
if (!pFilter)
// We expect this to be a single filter.
continue;
GroupFieldMapType::const_iterator itrGrp = aGroupFieldIds.find(itr->mnFieldIndex);
if (itrGrp == itrGrpEnd)
{
if (IsNumGroupDimension(itr->mnFieldIndex))
{
// internal number group field
const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[itr->mnFieldIndex];
const ScDPDateGroupHelper* pDateHelper = rNumGrpDim.GetDateHelper();
if (!pDateHelper)
{
// What do we do here !?
continue;
}
ScDPCacheTable::Criterion aCri;
aCri.mnFieldIndex = itr->mnFieldIndex;
aCri.mpFilter.reset(new ScDPGroupDateFilter(
pFilter->getMatchValue(), pDateHelper->GetDatePart(),
pDoc->GetFormatTable()->GetNullDate(), &pDateHelper->GetNumInfo()));
aNewCriteria.push_back(aCri);
}
else
{
// This is a regular source field.
aNewCriteria.push_back(*itr);
}
}
else
{
// This is an ordinary group field or external number group field.
const ScDPGroupDimension* pGrpDim = itrGrp->second;
long nSrcDim = pGrpDim->GetSourceDim();
const ScDPDateGroupHelper* pDateHelper = pGrpDim->GetDateHelper();
if (pDateHelper)
{
// external number group
ScDPCacheTable::Criterion aCri;
aCri.mnFieldIndex = nSrcDim; // use the source dimension, not the group dimension.
aCri.mpFilter.reset(new ScDPGroupDateFilter(
pFilter->getMatchValue(), pDateHelper->GetDatePart(),
pDoc->GetFormatTable()->GetNullDate(), &pDateHelper->GetNumInfo()));
aNewCriteria.push_back(aCri);
}
else
{
// normal group
// Note that each group dimension may have multiple group names!
size_t nGroupItemCount = pGrpDim->GetItemCount();
for (size_t i = 0; i < nGroupItemCount; ++i)
{
const ScDPGroupItem* pGrpItem = pGrpDim->GetGroupByIndex(i);
// Wang Xu Ming -- 2009-6-9
// DataPilot Migration
ScDPItemData aName( pFilter->getMatchString(),pFilter->getMatchValue(),pFilter->hasValue()) ;
/*aName.aString = pFilter->getMatchString();
aName.fValue = pFilter->getMatchValue();
aName.bHasValue = pFilter->hasValue();*/
// End Comments
if (!pGrpItem || !pGrpItem->GetName().IsCaseInsEqual(aName))
continue;
ScDPCacheTable::Criterion aCri;
aCri.mnFieldIndex = nSrcDim;
aCri.mpFilter.reset(new ScDPCacheTable::GroupFilter());
ScDPCacheTable::GroupFilter* pGrpFilter =
static_cast<ScDPCacheTable::GroupFilter*>(aCri.mpFilter.get());
pGrpItem->FillGroupFilter(*pGrpFilter);
aNewCriteria.push_back(aCri);
}
}
}
}
rCriteria.swap(aNewCriteria);
}
void ScDPGroupTableData::FilterCacheTable(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims)
{
vector<ScDPCacheTable::Criterion> aNewCriteria(rCriteria);
ModifyFilterCriteria(aNewCriteria);
pSourceData->FilterCacheTable(aNewCriteria, rCatDims);
}
void ScDPGroupTableData::GetDrillDownData(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims, Sequence< Sequence<Any> >& rData)
{
vector<ScDPCacheTable::Criterion> aNewCriteria(rCriteria);
ModifyFilterCriteria(aNewCriteria);
pSourceData->GetDrillDownData(aNewCriteria, rCatDims, rData);
}
void ScDPGroupTableData::CalcResults(CalcInfo& rInfo, bool bAutoShow)
{
// #i111435# Inside FillRowDataFromCacheTable/GetItemData, virtual methods
// getIsDataLayoutDimension and GetSourceDim are used, so it has to be called
// with original rInfo, containing dimension indexes of the grouped data.
const ScDPCacheTable& rCacheTable = pSourceData->GetCacheTable();
sal_Int32 nRowSize = rCacheTable.getRowSize();
for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
{
if (!rCacheTable.isRowActive(nRow))
continue;
CalcRowData aData;
FillRowDataFromCacheTable(nRow, rCacheTable, rInfo, aData);
if ( !rInfo.aColLevelDims.empty() )
FillGroupValues(&aData.aColData[0], rInfo.aColLevelDims.size(), &rInfo.aColLevelDims[0]);
if ( !rInfo.aRowLevelDims.empty() )
FillGroupValues(&aData.aRowData[0], rInfo.aRowLevelDims.size(), &rInfo.aRowLevelDims[0]);
if ( !rInfo.aPageDims.empty() )
FillGroupValues(&aData.aPageData[0], rInfo.aPageDims.size(), &rInfo.aPageDims[0]);
ProcessRowData(rInfo, aData, bAutoShow);
}
}
const ScDPCacheTable& ScDPGroupTableData::GetCacheTable() const
{
return pSourceData->GetCacheTable();
}
void ScDPGroupTableData::FillGroupValues( /*ScDPItemData* pItemData*/ SCROW* pItemDataIndex, long nCount, const long* pDims )
{
long nGroupedColumns = aGroups.size();
ScDPTableDataCache* pCache = GetCacheTable().GetCache();
for (long nDim=0; nDim<nCount; nDim++)
{
const ScDPDateGroupHelper* pDateHelper = NULL;
long nColumn = pDims[nDim];
long nSourceDim = nColumn;
if ( nColumn >= nSourceCount && nColumn < nSourceCount + nGroupedColumns )
{
const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
nSourceDim= rGroupDim.GetSourceDim();
pDateHelper = rGroupDim.GetDateHelper();
if ( !pDateHelper ) // date is handled below
{
const ScDPGroupItem* pGroupItem = rGroupDim.GetGroupForData( *GetMemberById( nSourceDim, pItemDataIndex[nDim] ));
if ( pGroupItem )
pItemDataIndex[nDim] = pCache->GetAdditionalItemID( pGroupItem->GetName() );
}
}
else if ( IsNumGroupDimension( nColumn ) )
{
pDateHelper = pNumGroups[nColumn].GetDateHelper();
if ( !pDateHelper ) // date is handled below
{
const ScDPItemData* pData = pCache->GetItemDataById( (SCCOL)nSourceDim, pItemDataIndex[nDim]);
if ( pData ->IsValue() )
{
ScDPNumGroupInfo aNumInfo;
bool bHasNonInteger = false;
sal_Unicode cDecSeparator = 0;
GetNumGroupInfo( nColumn, aNumInfo, bHasNonInteger, cDecSeparator );
double fGroupValue;
String aGroupName = lcl_GetNumGroupForValue( pData->GetValue(),
aNumInfo, bHasNonInteger, cDecSeparator, fGroupValue, pDoc );
ScDPItemData aItemData ( aGroupName, fGroupValue, sal_True ) ;
pItemDataIndex[nDim] = pCache->GetAdditionalItemID( aItemData );
}
// else (textual) keep original value
}
}
if ( pDateHelper )
{
const ScDPItemData* pData = GetCacheTable().GetCache()->GetItemDataById( (SCCOL)nSourceDim, pItemDataIndex[nDim]);
if ( pData ->IsValue() )
{
sal_Int32 nPartValue = lcl_GetDatePartValue(
pData->GetValue(), pDateHelper->GetDatePart(), pDoc->GetFormatTable(),
&pDateHelper->GetNumInfo() );
// Wang Xu Ming -- 2009-9-7
// DataPilot Migration - Cache&&Performance
//String aName = lcl_GetDateGroupName( pDateHelper, nPartValue, pDoc->GetFormatTable() );
ScDPItemData aItemData( pDateHelper->GetDatePart(), String(), nPartValue, ScDPItemData::MK_DATA|ScDPItemData::MK_VAL|ScDPItemData::MK_DATEPART );
pItemDataIndex[nDim] = GetCacheTable().GetCache()->GetAdditionalItemID( aItemData );
// End Comments
}
}
}
}
sal_Bool ScDPGroupTableData::IsBaseForGroup(long nDim) const
{
for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
{
const ScDPGroupDimension& rDim = *aIter;
if ( rDim.GetSourceDim() == nDim )
return sal_True;
}
return sal_False;
}
long ScDPGroupTableData::GetGroupBase(long nGroupDim) const
{
for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
{
const ScDPGroupDimension& rDim = *aIter;
if ( rDim.GetGroupDim() == nGroupDim )
return rDim.GetSourceDim();
}
return -1; // none
}
sal_Bool ScDPGroupTableData::IsNumOrDateGroup(long nDimension) const
{
// Virtual method from ScDPTableData, used in result data to force text labels.
if ( nDimension < nSourceCount )
{
return pNumGroups[nDimension].GetInfo().Enable ||
pNumGroups[nDimension].GetDateHelper();
}
for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
{
const ScDPGroupDimension& rDim = *aIter;
if ( rDim.GetGroupDim() == nDimension )
return ( rDim.GetDateHelper() != NULL );
}
return sal_False;
}
sal_Bool ScDPGroupTableData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex,
const ScDPItemData& rBaseData, long nBaseIndex ) const
{
for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
{
const ScDPGroupDimension& rDim = *aIter;
if ( rDim.GetGroupDim() == nGroupIndex && rDim.GetSourceDim() == nBaseIndex )
{
const ScDPDateGroupHelper* pGroupDateHelper = rDim.GetDateHelper();
if ( pGroupDateHelper )
{
//! transform rBaseData (innermost date part)
//! -> always do "HasCommonElement" style comparison
//! (only Quarter, Month, Day affected)
const ScDPDateGroupHelper* pBaseDateHelper = NULL;
if ( nBaseIndex < nSourceCount )
pBaseDateHelper = pNumGroups[nBaseIndex].GetDateHelper();
// If there's a date group dimension, the base dimension must have
// date group information, too.
if ( !pBaseDateHelper )
{
DBG_ERROR( "mix of date and non-date groups" );
return sal_True;
}
sal_Int32 nGroupPart = pGroupDateHelper->GetDatePart();
sal_Int32 nBasePart = pBaseDateHelper->GetDatePart();
return lcl_DateContained( nGroupPart, rGroupData, nBasePart, rBaseData );
}
else
{
// If the item is in a group, only that group is valid.
// If the item is not in any group, its own name is valid.
const ScDPGroupItem* pGroup = rDim.GetGroupForData( rBaseData );
return pGroup ? pGroup->GetName().IsCaseInsEqual( rGroupData ) :
rGroupData.IsCaseInsEqual( rBaseData );
}
}
}
DBG_ERROR("IsInGroup: no group dimension found");
return sal_True;
}
sal_Bool ScDPGroupTableData::HasCommonElement( const ScDPItemData& rFirstData, long nFirstIndex,
const ScDPItemData& rSecondData, long nSecondIndex ) const
{
const ScDPGroupDimension* pFirstDim = NULL;
const ScDPGroupDimension* pSecondDim = NULL;
for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
{
const ScDPGroupDimension* pDim = &(*aIter);
if ( pDim->GetGroupDim() == nFirstIndex )
pFirstDim = pDim;
else if ( pDim->GetGroupDim() == nSecondIndex )
pSecondDim = pDim;
}
if ( pFirstDim && pSecondDim )
{
const ScDPDateGroupHelper* pFirstDateHelper = pFirstDim->GetDateHelper();
const ScDPDateGroupHelper* pSecondDateHelper = pSecondDim->GetDateHelper();
if ( pFirstDateHelper || pSecondDateHelper )
{
// If one is a date group dimension, the other one must be, too.
if ( !pFirstDateHelper || !pSecondDateHelper )
{
DBG_ERROR( "mix of date and non-date groups" );
return sal_True;
}
sal_Int32 nFirstPart = pFirstDateHelper->GetDatePart();
sal_Int32 nSecondPart = pSecondDateHelper->GetDatePart();
return lcl_DateContained( nFirstPart, rFirstData, nSecondPart, rSecondData );
}
const ScDPGroupItem* pFirstItem = pFirstDim->GetGroupForName( rFirstData );
const ScDPGroupItem* pSecondItem = pSecondDim->GetGroupForName( rSecondData );
if ( pFirstItem && pSecondItem )
{
// two existing groups -> sal_True if they have a common element
return pFirstItem->HasCommonElement( *pSecondItem );
}
else if ( pFirstItem )
{
// "automatic" group contains only its own name
return pFirstItem->HasElement( rSecondData );
}
else if ( pSecondItem )
{
// "automatic" group contains only its own name
return pSecondItem->HasElement( rFirstData );
}
else
{
// no groups -> sal_True if equal
return rFirstData.IsCaseInsEqual( rSecondData );
}
}
DBG_ERROR("HasCommonElement: no group dimension found");
return sal_True;
}
long ScDPGroupTableData::GetSourceDim( long nDim )
{
if ( getIsDataLayoutDimension( nDim ) )
return nSourceCount;
if ( nDim >= nSourceCount && nDim < nSourceCount +(long) aGroups.size() )
{
const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
return rGroupDim.GetSourceDim();
}
return nDim;
}
long ScDPGroupTableData::Compare( long nDim, long nDataId1, long nDataId2)
{
if ( getIsDataLayoutDimension(nDim) )
return 0;
return ScDPItemData::Compare( *GetMemberById(nDim, nDataId1),*GetMemberById(nDim, nDataId2) );
}
// -----------------------------------------------------------------------