/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" // INCLUDE --------------------------------------------------------------- #include #include #include "dptabdat.hxx" #include "dptabres.hxx" #include "dptabsrc.hxx" #include "global.hxx" #include "subtotal.hxx" #include "globstr.hrc" #include "datauno.hxx" // ScDataUnoConversion #include "document.hxx" // for DumpState only! #include #include //! Test !!! #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; using ::std::vector; using ::std::pair; using ::std::hash_map; using ::com::sun::star::uno::Sequence; using ::rtl::OUString; // ----------------------------------------------------------------------- SV_IMPL_PTRARR( ScDPDataMembers, ScDPDataMemberPtr ); // ----------------------------------------------------------------------- static sal_uInt16 nFuncStrIds[12] = // passend zum enum ScSubTotalFunc { 0, // SUBTOTAL_FUNC_NONE STR_FUN_TEXT_AVG, // SUBTOTAL_FUNC_AVE STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT2 STR_FUN_TEXT_MAX, // SUBTOTAL_FUNC_MAX STR_FUN_TEXT_MIN, // SUBTOTAL_FUNC_MIN STR_FUN_TEXT_PRODUCT, // SUBTOTAL_FUNC_PROD STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STD STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STDP STR_FUN_TEXT_SUM, // SUBTOTAL_FUNC_SUM STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VAR STR_FUN_TEXT_VAR // SUBTOTAL_FUNC_VARP }; namespace { template < typename T > void lcl_ResizePointVector( T & vec, size_t nSize ) { for ( size_t i = 0 ; i < vec.size(); i++ ) { if ( vec[i] ) delete vec[i]; } vec.resize( nSize, NULL ); } sal_Bool lcl_SearchMember( const std::vector & list, SCROW nOrder, SCROW& rIndex) { rIndex = list.size(); sal_Bool bFound = sal_False; SCROW nLo = 0; SCROW nHi = list.size() - 1; SCROW nIndex; while (nLo <= nHi) { nIndex = (nLo + nHi) / 2; if ( list[nIndex]->GetOrder() < nOrder ) nLo = nIndex + 1; else { nHi = nIndex - 1; if ( list[nIndex]->GetOrder() == nOrder ) { bFound = sal_True; nLo = nIndex; } } } rIndex = nLo; return bFound; } } // ----------------------------------------------------------------------- // // function objects for sorting of the column and row members: // class ScDPRowMembersOrder { ScDPResultDimension& rDimension; long nMeasure; sal_Bool bAscending; public: ScDPRowMembersOrder( ScDPResultDimension& rDim, long nM, sal_Bool bAsc ) : rDimension(rDim), nMeasure(nM), bAscending(bAsc) {} ~ScDPRowMembersOrder() {} sal_Bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const; }; class ScDPColMembersOrder { ScDPDataDimension& rDimension; long nMeasure; sal_Bool bAscending; public: ScDPColMembersOrder( ScDPDataDimension& rDim, long nM, sal_Bool bAsc ) : rDimension(rDim), nMeasure(nM), bAscending(bAsc) {} ~ScDPColMembersOrder() {} sal_Bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const; }; static sal_Bool lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure, sal_Bool bAscending ) { // members can be NULL if used for rows ScDPSubTotalState aEmptyState; const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL; const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL; sal_Bool bError1 = pAgg1 && pAgg1->HasError(); sal_Bool bError2 = pAgg2 && pAgg2->HasError(); if ( bError1 ) { if ( bError2 ) return sal_False; // equal else return sal_False; // errors are always sorted at the end } else if ( bError2 ) return sal_True; // errors are always sorted at the end else { double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0 double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0; // compare values // don't have to check approxEqual, as this is the only sort criterion return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 ); } } static sal_Bool lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure ) { // members can be NULL if used for rows ScDPSubTotalState aEmptyState; const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL; const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL; sal_Bool bError1 = pAgg1 && pAgg1->HasError(); sal_Bool bError2 = pAgg2 && pAgg2->HasError(); if ( bError1 ) { if ( bError2 ) return sal_True; // equal else return sal_False; } else if ( bError2 ) return sal_False; else { double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0 double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0; // compare values // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used return rtl::math::approxEqual( fVal1, fVal2 ); } } sal_Bool ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const { const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1); const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2); // Wang Xu Ming -- 3/17/2009 // make the hide item to the largest order. if ( !pMember1->IsVisible() || !pMember2->IsVisible() ) return pMember1->IsVisible(); const ScDPDataMember* pDataMember1 = pMember1->GetDataRoot() ; const ScDPDataMember* pDataMember2 = pMember2->GetDataRoot(); // End Comments // GetDataRoot can be NULL if there was no data. // IsVisible == sal_False can happen after AutoShow. return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending ); } sal_Bool ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const { ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1); ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2); // Wang Xu Ming -- 2009-6-17 sal_Bool bHide1 = pDataMember1 && !pDataMember1->IsVisible(); sal_Bool bHide2 = pDataMember2 && !pDataMember2->IsVisible(); if ( bHide1 || bHide2 ) return !bHide1; // End Comments return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending ); } // ----------------------------------------------------------------------- ScDPInitState::ScDPInitState() : nCount( 0 ) { pIndex = new long[SC_DAPI_MAXFIELDS]; pData = new SCROW[SC_DAPI_MAXFIELDS]; } ScDPInitState::~ScDPInitState() { delete[] pIndex; delete[] pData; } void ScDPInitState::AddMember( long nSourceIndex, SCROW nMember ) { DBG_ASSERT( nCount < SC_DAPI_MAXFIELDS, "too many InitState members" ); if ( nCount < SC_DAPI_MAXFIELDS ) { pIndex[nCount] = nSourceIndex; pData[nCount] = nMember; ++nCount; } } void ScDPInitState::RemoveMember() { DBG_ASSERT( nCount > 0, "RemoveColIndex without index" ); if ( nCount > 0 ) --nCount; } SCROW ScDPInitState::GetNameIdForIndex( long nIndexValue ) const { for (long i=0; iSetString( nCol++, nRow, nTab, rType ); pDoc->SetString( nCol++, nRow, nTab, rName ); while ( pAggData ) { pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() ); pAggData = pAggData->GetExistingChild(); } rPos.SetRow( nRow + 1 ); } void lcl_Indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos ) { SCCOL nCol = rPos.Col(); SCTAB nTab = rPos.Tab(); String aString; for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++) { pDoc->GetString( nCol, nRow, nTab, aString ); if ( aString.Len() ) { aString.InsertAscii( " ", 0 ); pDoc->SetString( nCol, nRow, nTab, aString ); } } } // ----------------------------------------------------------------------- ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember* pColRoot, ScDPResultMember* pRowRoot ) : pColResRoot( pColRoot ), pRowResRoot( pRowRoot ), nColIndexPos( 0 ), nRowIndexPos( 0 ) { pColVisible = new long[SC_DAPI_MAXFIELDS+1]; pColIndexes = new long[SC_DAPI_MAXFIELDS+1]; pRowVisible = new long[SC_DAPI_MAXFIELDS+1]; pRowIndexes = new long[SC_DAPI_MAXFIELDS+1]; pColIndexes[0] = -1; pRowIndexes[0] = -1; } ScDPRunningTotalState::~ScDPRunningTotalState() { delete[] pColVisible; delete[] pColIndexes; delete[] pRowVisible; delete[] pRowIndexes; } void ScDPRunningTotalState::AddColIndex( long nVisible, long nSorted ) { DBG_ASSERT( nColIndexPos < SC_DAPI_MAXFIELDS, "too many column indexes" ); if ( nColIndexPos < SC_DAPI_MAXFIELDS ) { pColVisible[nColIndexPos] = nVisible; pColIndexes[nColIndexPos] = nSorted; pColVisible[nColIndexPos+1] = -1; pColIndexes[nColIndexPos+1] = -1; ++nColIndexPos; } } void ScDPRunningTotalState::AddRowIndex( long nVisible, long nSorted ) { DBG_ASSERT( nRowIndexPos < SC_DAPI_MAXFIELDS, "too many row indexes" ); if ( nRowIndexPos < SC_DAPI_MAXFIELDS ) { pRowVisible[nRowIndexPos] = nVisible; pRowIndexes[nRowIndexPos] = nSorted; pRowVisible[nRowIndexPos+1] = -1; pRowIndexes[nRowIndexPos+1] = -1; ++nRowIndexPos; } } void ScDPRunningTotalState::RemoveColIndex() { DBG_ASSERT( nColIndexPos > 0, "RemoveColIndex without index" ); if ( nColIndexPos > 0 ) { --nColIndexPos; pColVisible[nColIndexPos] = -1; pColIndexes[nColIndexPos] = -1; } } void ScDPRunningTotalState::RemoveRowIndex() { DBG_ASSERT( nRowIndexPos > 0, "RemoveRowIndex without index" ); if ( nRowIndexPos > 0 ) { --nRowIndexPos; pRowVisible[nRowIndexPos] = -1; pRowIndexes[nRowIndexPos] = -1; } } // ----------------------------------------------------------------------- ScDPRelativePos::ScDPRelativePos( long nBase, long nDir ) : nBasePos( nBase ), nDirection( nDir ) { } // ----------------------------------------------------------------------- void ScDPAggData::Update( const ScDPValueData& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState ) { if (nCount<0) // error? return; // nothing more... if ( rNext.nType == SC_VALTYPE_EMPTY ) return; if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE && rSubState.eColForce != rSubState.eRowForce ) return; if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce; if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce; if ( eFunc == SUBTOTAL_FUNC_NONE ) return; if ( eFunc != SUBTOTAL_FUNC_CNT2 ) // CNT2 counts everything, incl. strings and errors { if ( rNext.nType == SC_VALTYPE_ERROR ) { nCount = -1; // -1 for error (not for CNT2) return; } if ( rNext.nType == SC_VALTYPE_STRING ) return; // ignore } ++nCount; // for all functions switch (eFunc) { case SUBTOTAL_FUNC_SUM: case SUBTOTAL_FUNC_AVE: if ( !SubTotal::SafePlus( fVal, rNext.fValue ) ) nCount = -1; // -1 for error break; case SUBTOTAL_FUNC_PROD: if ( nCount == 1 ) // copy first value (fVal is initialized to 0) fVal = rNext.fValue; else if ( !SubTotal::SafeMult( fVal, rNext.fValue ) ) nCount = -1; // -1 for error break; case SUBTOTAL_FUNC_CNT: case SUBTOTAL_FUNC_CNT2: // nothing more than incrementing nCount break; case SUBTOTAL_FUNC_MAX: if ( nCount == 1 || rNext.fValue > fVal ) fVal = rNext.fValue; break; case SUBTOTAL_FUNC_MIN: if ( nCount == 1 || rNext.fValue < fVal ) fVal = rNext.fValue; break; case SUBTOTAL_FUNC_STD: case SUBTOTAL_FUNC_STDP: case SUBTOTAL_FUNC_VAR: case SUBTOTAL_FUNC_VARP: { // fAux is used to sum up squares if ( !SubTotal::SafePlus( fVal, rNext.fValue ) ) nCount = -1; // -1 for error double fAdd = rNext.fValue; if ( !SubTotal::SafeMult( fAdd, rNext.fValue ) || !SubTotal::SafePlus( fAux, fAdd ) ) nCount = -1; // -1 for error } break; default: DBG_ERROR("invalid function"); } } void ScDPAggData::Calculate( ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState ) { // calculate the original result // (without reference value, used as the basis for reference value calculation) // called several times at the cross-section of several subtotals - don't calculate twice then if ( IsCalculated() ) return; if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce; if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce; if ( eFunc == SUBTOTAL_FUNC_NONE ) // this happens when there is no data dimension { nCount = SC_DPAGG_RESULT_EMPTY; // make sure there's a valid state for HasData etc. return; } // check the error conditions for the selected function sal_Bool bError = sal_False; switch (eFunc) { case SUBTOTAL_FUNC_SUM: case SUBTOTAL_FUNC_PROD: case SUBTOTAL_FUNC_CNT: case SUBTOTAL_FUNC_CNT2: bError = ( nCount < 0 ); // only real errors break; case SUBTOTAL_FUNC_AVE: case SUBTOTAL_FUNC_MAX: case SUBTOTAL_FUNC_MIN: case SUBTOTAL_FUNC_STDP: case SUBTOTAL_FUNC_VARP: bError = ( nCount <= 0 ); // no data is an error break; case SUBTOTAL_FUNC_STD: case SUBTOTAL_FUNC_VAR: bError = ( nCount < 2 ); // need at least 2 values break; default: DBG_ERROR("invalid function"); } // calculate the selected function double fResult = 0.0; if ( !bError ) { switch (eFunc) { case SUBTOTAL_FUNC_MAX: case SUBTOTAL_FUNC_MIN: case SUBTOTAL_FUNC_SUM: case SUBTOTAL_FUNC_PROD: // different error conditions are handled above fResult = fVal; break; case SUBTOTAL_FUNC_CNT: case SUBTOTAL_FUNC_CNT2: fResult = nCount; break; case SUBTOTAL_FUNC_AVE: if ( nCount > 0 ) fResult = fVal / (double) nCount; break; //! use safe mul for fVal * fVal case SUBTOTAL_FUNC_STD: if ( nCount >= 2 ) fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1)); break; case SUBTOTAL_FUNC_VAR: if ( nCount >= 2 ) fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1); break; case SUBTOTAL_FUNC_STDP: if ( nCount > 0 ) fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)nCount); break; case SUBTOTAL_FUNC_VARP: if ( nCount > 0 ) fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)nCount; break; default: DBG_ERROR("invalid function"); } } sal_Bool bEmpty = ( nCount == 0 ); // no data // store the result // Empty is checked first, so empty results are shown empty even for "average" etc. // If these results should be treated as errors in reference value calculations, // a separate state value (EMPTY_ERROR) is needed. // Now, for compatibility, empty "average" results are counted as 0. if ( bEmpty ) nCount = SC_DPAGG_RESULT_EMPTY; else if ( bError ) nCount = SC_DPAGG_RESULT_ERROR; else nCount = SC_DPAGG_RESULT_VALID; if ( bEmpty || bError ) fResult = 0.0; // default, in case the state is later modified // fprintf(stdout, "ScDPAggData::Calculate: result = %g\n", fResult);fflush(stdout); fVal = fResult; // used directly from now on fAux = 0.0; // used for running total or original result of reference value } sal_Bool ScDPAggData::IsCalculated() const { return ( nCount <= SC_DPAGG_RESULT_EMPTY ); } double ScDPAggData::GetResult() const { DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); return fVal; // use calculated value } sal_Bool ScDPAggData::HasError() const { DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); return ( nCount == SC_DPAGG_RESULT_ERROR ); } sal_Bool ScDPAggData::HasData() const { DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); return ( nCount != SC_DPAGG_RESULT_EMPTY ); // values or error } void ScDPAggData::SetResult( double fNew ) { DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); fVal = fNew; // don't reset error flag } void ScDPAggData::SetError() { DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); nCount = SC_DPAGG_RESULT_ERROR; } void ScDPAggData::SetEmpty( sal_Bool bSet ) { DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); if ( bSet ) nCount = SC_DPAGG_RESULT_EMPTY; else nCount = SC_DPAGG_RESULT_VALID; } double ScDPAggData::GetAuxiliary() const { // after Calculate, fAux is used as auxiliary value for running totals and reference values DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); return fAux; } void ScDPAggData::SetAuxiliary( double fNew ) { // after Calculate, fAux is used as auxiliary value for running totals and reference values DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); fAux = fNew; } ScDPAggData* ScDPAggData::GetChild() { if (!pChild) pChild = new ScDPAggData; return pChild; } void ScDPAggData::Reset() { fVal = 0.0; fAux = 0.0; nCount = SC_DPAGG_EMPTY; delete pChild; pChild = NULL; } // ----------------------------------------------------------------------- ScDPRowTotals::ScDPRowTotals() : bIsInColRoot( sal_False ) { } ScDPRowTotals::~ScDPRowTotals() { } ScDPAggData* lcl_GetChildTotal( ScDPAggData* pFirst, long nMeasure ) { DBG_ASSERT( nMeasure >= 0, "GetColTotal: no measure" ); ScDPAggData* pAgg = pFirst; long nSkip = nMeasure; // subtotal settings are ignored - colum/row totals exist once per measure for ( long nPos=0; nPosGetChild(); // column total is constructed empty - children need to be created if ( !pAgg->IsCalculated() ) { // for first use, simulate an empty calculation ScDPSubTotalState aEmptyState; pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState ); } return pAgg; } ScDPAggData* ScDPRowTotals::GetRowTotal( long nMeasure ) { return lcl_GetChildTotal( &aRowTotal, nMeasure ); } ScDPAggData* ScDPRowTotals::GetGrandTotal( long nMeasure ) { return lcl_GetChildTotal( &aGrandTotal, nMeasure ); } // ----------------------------------------------------------------------- static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, long nFuncNo ) { ScSubTotalFunc eRet = SUBTOTAL_FUNC_NONE; if ( pLevel ) { //! direct access via ScDPLevel uno::Sequence aSeq = pLevel->getSubTotals(); long nSequence = aSeq.getLength(); if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO ) { // For manual subtotals, "automatic" is added as first function. // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be // returned as the first function then. --nFuncNo; // keep NONE for first (check below), move the other entries } if ( nFuncNo >= 0 && nFuncNo < nSequence ) { sheet::GeneralFunction eUser = aSeq.getConstArray()[nFuncNo]; if (eUser != sheet::GeneralFunction_AUTO) eRet = ScDataUnoConversion::GeneralToSubTotal( eUser ); } } return eRet; } // ----------------------------------------------------------------------- ScDPResultData::ScDPResultData( ScDPSource* pSrc ) : //! Ref pSource( pSrc ), nMeasCount( 0 ), pMeasFuncs( NULL ), pMeasRefs( NULL ), pMeasRefOrient( NULL ), pMeasNames( NULL ), bLateInit( sal_False ), bDataAtCol( sal_False ), bDataAtRow( sal_False ) { lcl_ResizePointVector( mpDimMembers , SC_DAPI_MAXFIELDS ); } ScDPResultData::~ScDPResultData() { delete[] pMeasFuncs; delete[] pMeasRefs; delete[] pMeasRefOrient; delete[] pMeasNames; lcl_ResizePointVector( mpDimMembers , 0 ); } void ScDPResultData::SetMeasureData( long nCount, const ScSubTotalFunc* pFunctions, const sheet::DataPilotFieldReference* pRefs, const sal_uInt16* pRefOrient, const String* pNames ) { delete[] pMeasFuncs; delete[] pMeasRefs; delete[] pMeasRefOrient; delete[] pMeasNames; if ( nCount ) { nMeasCount = nCount; pMeasFuncs = new ScSubTotalFunc[nCount]; pMeasRefs = new sheet::DataPilotFieldReference[nCount]; pMeasRefOrient = new sal_uInt16[nCount]; pMeasNames = new String[nCount]; for (long i=0; iGetDataDimension(nMeasure); if (pDataDim) { const OUString* pLayoutName = pDataDim->GetLayoutName(); if (pLayoutName) return *pLayoutName; } String aRet; ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ? GetMeasureFunction(nMeasure) : eForceFunc; sal_uInt16 nId = nFuncStrIds[eFunc]; if (nId) { aRet += ScGlobal::GetRscString(nId); // function name aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " )); } aRet += pMeasNames[nMeasure]; // field name return aRet; } } String ScDPResultData::GetMeasureDimensionName(long nMeasure) const { if ( nMeasure < 0 ) { DBG_ERROR("GetMeasureDimensionName: negative"); return String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("***")); } return pSource->GetDataDimName( nMeasure ); } sal_Bool ScDPResultData::IsBaseForGroup( long nDim ) const { return pSource->GetData()->IsBaseForGroup( nDim ); } long ScDPResultData::GetGroupBase( long nGroupDim ) const { return pSource->GetData()->GetGroupBase( nGroupDim ); } sal_Bool ScDPResultData::IsNumOrDateGroup( long nDim ) const { return pSource->GetData()->IsNumOrDateGroup( nDim ); } sal_Bool ScDPResultData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex, long nBaseDataId, long nBaseIndex ) const { const ScDPItemData* pData = pSource->GetItemDataById( nGroupIndex , nBaseDataId); if ( pData ) return pSource->GetData()->IsInGroup( rGroupData, nGroupIndex, *pData , nBaseIndex ); else return sal_False; } sal_Bool ScDPResultData::IsInGroup( SCROW nGroupDataId, long nGroupIndex, const ScDPItemData& rBaseData, long nBaseIndex ) const { const ScDPItemData* pGroupData = pSource->GetItemDataById( nGroupIndex , nGroupDataId); if ( pGroupData ) return pSource->GetData()->IsInGroup( *pGroupData, nGroupIndex, rBaseData , nBaseIndex ); else return sal_False; } sal_Bool ScDPResultData::HasCommonElement(/* const ScDPItemData& rFirstData*/SCROW nFirstDataId, long nFirstIndex, const ScDPItemData& rSecondData, long nSecondIndex ) const { const ScDPItemData* pFirstData = pSource->GetItemDataById( nFirstIndex , nFirstDataId); if ( pFirstData ) return pSource->GetData()->HasCommonElement( *pFirstData, nFirstIndex, rSecondData, nSecondIndex ); else return sal_False; } const ScDPSource* ScDPResultData::GetSource() const { return pSource; } ResultMembers* ScDPResultData::GetDimResultMembers( long nDim , ScDPDimension* pDim, ScDPLevel* pLevel) const { if ( mpDimMembers[ nDim ] == NULL ) { //long nDimSource = pDim->GetDimension(); ResultMembers* pResultMembers = new ResultMembers(); // global order is used to initialize aMembers, so it doesn't have to be looked at later const ScMemberSortOrder& rGlobalOrder = pLevel->GetGlobalOrder(); ScDPMembers* pMembers = pLevel->GetMembersObject(); long nMembCount = pMembers->getCount(); for ( long i=0; igetByIndex(nSorted); if ( NULL == pResultMembers->FindMember( pMember->GetItemDataId() ) ) { ScDPParentDimData* pNew = new ScDPParentDimData( i, pDim, pLevel, pMember ); pResultMembers->InsertMember( pNew ); } } mpDimMembers[ nDim ] = pResultMembers; } return mpDimMembers[ nDim ]; } // ----------------------------------------------------------------------- ScDPResultMember::ScDPResultMember( const ScDPResultData* pData, const ScDPParentDimData& rParentDimData , sal_Bool bForceSub ) : pResultData( pData ), aParentDimData( rParentDimData ), pChildDimension( NULL ), pDataRoot( NULL ), bHasElements( sal_False ), bForceSubTotal( bForceSub ), bHasHiddenDetails( sal_False ), bInitialized( sal_False ), bAutoHidden( sal_False ), nMemberStep( 1 ) { // pParentLevel/pMemberDesc is 0 for root members } ScDPResultMember::ScDPResultMember( const ScDPResultData* pData, sal_Bool bForceSub ) : pResultData( pData ), pChildDimension( NULL ), pDataRoot( NULL ), bHasElements( sal_False ), bForceSubTotal( bForceSub ), bHasHiddenDetails( sal_False ), bInitialized( sal_False ), bAutoHidden( sal_False ), nMemberStep( 1 ) { } ScDPResultMember::~ScDPResultMember() { delete pChildDimension; delete pDataRoot; } String ScDPResultMember::GetName() const { const ScDPMember* pMemberDesc = GetDPMember(); if (pMemberDesc) return pMemberDesc->GetNameStr(); else return ScGlobal::GetRscString(STR_PIVOT_TOTAL); // root member } void ScDPResultMember::FillItemData( ScDPItemData& rData ) const { const ScDPMember* pMemberDesc = GetDPMember(); if (pMemberDesc) pMemberDesc->FillItemData( rData ); else rData.SetString( ScGlobal::GetRscString(STR_PIVOT_TOTAL) ); // root member } sal_Bool ScDPResultMember::IsNamedItem( SCROW nIndex ) const { //! store ScDPMember pointer instead of ScDPMember ??? const ScDPMember* pMemberDesc = GetDPMember(); if (pMemberDesc) return ((ScDPMember*)pMemberDesc)->IsNamedItem( nIndex ); return sal_False; } bool ScDPResultMember::IsValidEntry( const vector< SCROW >& aMembers ) const { if ( !IsValid() ) return false; const ScDPResultDimension* pChildDim = GetChildDimension(); if (pChildDim) { if (aMembers.size() < 2) return false; vector::const_iterator itr = aMembers.begin(); vector aChildMembers(++itr, aMembers.end()); return pChildDim->IsValidEntry(aChildMembers); } else return true; } void ScDPResultMember::InitFrom( const vector& ppDim, const vector& ppLev, size_t nPos, ScDPInitState& rInitState , sal_Bool bInitChild /*= sal_True */) { // with LateInit, initialize only those members that have data if ( pResultData->IsLateInit() ) return; bInitialized = sal_True; if (nPos >= ppDim.size()) return; // skip child dimension if details are not shown if ( GetDPMember() && !GetDPMember()->getShowDetails() ) { // Wang Xu Ming -- 2009-6-16 // Show DataLayout dimention nMemberStep = 1; while ( nPos < ppDim.size() ) { if ( ppDim[nPos] ->getIsDataLayoutDimension() ) { if ( !pChildDimension ) pChildDimension = new ScDPResultDimension( pResultData ); pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState , sal_False ); return; } else { //find next dim nPos ++; nMemberStep ++; } } // End Comments bHasHiddenDetails = sal_True; // only if there is a next dimension return; } if ( bInitChild ) { pChildDimension = new ScDPResultDimension( pResultData ); pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState, sal_True ); } } void ScDPResultMember::LateInitFrom( LateInitParams& rParams/*const vector& ppDim, const vector& ppLev*/, const vector< SCROW >& pItemData, size_t nPos, ScDPInitState& rInitState ) { // without LateInit, everything has already been initialized if ( !pResultData->IsLateInit() ) return; bInitialized = sal_True; if ( rParams.IsEnd( nPos ) /*nPos >= ppDim.size()*/) // No next dimension. Bail out. return; // skip child dimension if details are not shown if ( GetDPMember() && !GetDPMember()->getShowDetails() ) { // Wang Xu Ming -- 2009-6-16 // DataPilot Migration // Show DataLayout dimention nMemberStep = 1; while ( !rParams.IsEnd( nPos ) ) { if ( rParams.GetDim( nPos ) ->getIsDataLayoutDimension() ) { if ( !pChildDimension ) pChildDimension = new ScDPResultDimension( pResultData ); // #i111462# reset InitChild flag only for this child dimension's LateInitFrom call, // not for following members of parent dimensions sal_Bool bWasInitChild = rParams.GetInitChild(); rParams.SetInitChild( sal_False ); pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState ); rParams.SetInitChild( bWasInitChild ); return; } else { //find next dim nPos ++; nMemberStep ++; } } // End Comments bHasHiddenDetails = sal_True; // only if there is a next dimension return; } // LateInitFrom is called several times... if ( rParams.GetInitChild() ) { if ( !pChildDimension ) pChildDimension = new ScDPResultDimension( pResultData ); pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState ); } } sal_Bool ScDPResultMember::IsSubTotalInTitle(long nMeasure) const { sal_Bool bRet = sal_False; if ( pChildDimension && /*pParentLevel*/GetParentLevel() && /*pParentLevel*/GetParentLevel()->IsOutlineLayout() && /*pParentLevel*/GetParentLevel()->IsSubtotalsAtTop() ) { long nUserSubStart; long nSubTotals = GetSubTotalCount( &nUserSubStart ); nSubTotals -= nUserSubStart; // visible count if ( nSubTotals ) { if ( nMeasure == SC_DPMEASURE_ALL ) nSubTotals *= pResultData->GetMeasureCount(); // number of subtotals that will be inserted // only a single subtotal row will be shown in the outline title row if ( nSubTotals == 1 ) bRet = sal_True; } } return bRet; } long ScDPResultMember::GetSize(long nMeasure) const { if ( !IsVisible() ) return 0; const ScDPLevel* pParentLevel = GetParentLevel(); long nExtraSpace = 0; if ( pParentLevel && pParentLevel->IsAddEmpty() ) ++nExtraSpace; if ( pChildDimension ) { // outline layout takes up an extra row for the title only if subtotals aren't shown in that row if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) ) ++nExtraSpace; long nSize = pChildDimension->GetSize(nMeasure); long nUserSubStart; long nUserSubCount = GetSubTotalCount( &nUserSubStart ); nUserSubCount -= nUserSubStart; // for output size, use visible count if ( nUserSubCount ) { if ( nMeasure == SC_DPMEASURE_ALL ) nSize += pResultData->GetMeasureCount() * nUserSubCount; else nSize += nUserSubCount; } return nSize + nExtraSpace; } else { if ( nMeasure == SC_DPMEASURE_ALL ) return pResultData->GetMeasureCount() + nExtraSpace; else return 1 + nExtraSpace; } } sal_Bool ScDPResultMember::IsVisible() const { // not initialized -> shouldn't be there at all // (allocated only to preserve ordering) const ScDPLevel* pParentLevel = GetParentLevel(); return ( bHasElements || ( pParentLevel && pParentLevel->getShowEmpty() ) ) && IsValid() && bInitialized; } sal_Bool ScDPResultMember::IsValid() const { // non-Valid members are left out of calculation // was member set no invisible at the DataPilotSource? const ScDPMember* pMemberDesc =GetDPMember(); if ( pMemberDesc && !pMemberDesc->getIsVisible() ) return sal_False; if ( bAutoHidden ) return sal_False; return sal_True; } sal_Bool ScDPResultMember::HasHiddenDetails() const { // bHasHiddenDetails is set only if the "show details" flag is off, // and there was a child dimension to skip return bHasHiddenDetails; } long ScDPResultMember::GetSubTotalCount( long* pUserSubStart ) const { if ( pUserSubStart ) *pUserSubStart = 0; // default const ScDPLevel* pParentLevel = GetParentLevel(); if ( bForceSubTotal ) // set if needed for root members return 1; // grand total is always "automatic" else if ( pParentLevel ) { //! direct access via ScDPLevel uno::Sequence aSeq = pParentLevel->getSubTotals(); long nSequence = aSeq.getLength(); if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO ) { // For manual subtotals, always add "automatic" as first function // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc) ++nSequence; if ( pUserSubStart ) *pUserSubStart = 1; // visible subtotals start at 1 } return nSequence; } else return 0; } void ScDPResultMember::ProcessData( const vector< SCROW >& aChildMembers, const ScDPResultDimension* pDataDim, const vector< SCROW >& aDataMembers, const vector& aValues ) { SetHasElements(); if (pChildDimension) pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues ); if ( !pDataRoot ) { pDataRoot = new ScDPDataMember( pResultData, NULL ); if ( pDataDim ) pDataRoot->InitFrom( pDataDim ); // recursive } ScDPSubTotalState aSubState; // initial state long nUserSubCount = GetSubTotalCount(); // Calculate at least automatic if no subtotals are selected, // show only own values if there's no child dimension (innermost). if ( !nUserSubCount || !pChildDimension ) nUserSubCount = 1; const ScDPLevel* pParentLevel = GetParentLevel(); for (long nUserPos=0; nUserPos