| /************************************************************** |
| * |
| * 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 <tools/debug.hxx> |
| #include <rtl/math.hxx> |
| |
| #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 <math.h> |
| #include <float.h> //! Test !!! |
| #include <algorithm> |
| #include <hash_map> |
| |
| #include <com/sun/star/sheet/DataResultFlags.hpp> |
| #include <com/sun/star/sheet/MemberResultFlags.hpp> |
| #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> |
| #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp> |
| #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp> |
| #include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp> |
| #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp> |
| #include <com/sun/star/sheet/DataPilotFieldFilter.hpp> |
| |
| 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 <ScDPResultMember *>& 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; i<nCount; i++) |
| if ( pIndex[i] == nIndexValue ) |
| return pData[i]; |
| |
| return -1; // not found |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void lcl_DumpRow( const String& rType, const String& rName, const ScDPAggData* pAggData, |
| ScDocument* pDoc, ScAddress& rPos ) |
| { |
| SCCOL nCol = rPos.Col(); |
| SCROW nRow = rPos.Row(); |
| SCTAB nTab = rPos.Tab(); |
| pDoc->SetString( 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; nPos<nSkip; nPos++ ) |
| pAgg = pAgg->GetChild(); // 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<sheet::GeneralFunction> 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; i<nCount; i++) |
| { |
| pMeasFuncs[i] = pFunctions[i]; |
| pMeasRefs[i] = pRefs[i]; |
| pMeasRefOrient[i] = pRefOrient[i]; |
| pMeasNames[i] = pNames[i]; |
| } |
| } |
| else |
| { |
| // use one dummy measure |
| nMeasCount = 1; |
| pMeasFuncs = new ScSubTotalFunc[1]; |
| pMeasFuncs[0] = SUBTOTAL_FUNC_NONE; |
| pMeasRefs = new sheet::DataPilotFieldReference[1]; // default ctor is ok |
| pMeasRefOrient = new sal_uInt16[1]; |
| pMeasRefOrient[0] = sheet::DataPilotFieldOrientation_HIDDEN; |
| pMeasNames = new String[1]; |
| pMeasNames[0] = ScGlobal::GetRscString( STR_EMPTYDATA ); |
| } |
| } |
| |
| void ScDPResultData::SetDataLayoutOrientation( sal_uInt16 nOrient ) |
| { |
| bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN ); |
| bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW ); |
| } |
| |
| void ScDPResultData::SetLateInit( sal_Bool bSet ) |
| { |
| bLateInit = bSet; |
| } |
| |
| long ScDPResultData::GetColStartMeasure() const |
| { |
| if ( nMeasCount == 1 ) return 0; |
| return bDataAtCol ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY; |
| } |
| |
| long ScDPResultData::GetRowStartMeasure() const |
| { |
| if ( nMeasCount == 1 ) return 0; |
| return bDataAtRow ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY; |
| } |
| |
| ScSubTotalFunc ScDPResultData::GetMeasureFunction(long nMeasure) const |
| { |
| DBG_ASSERT( pMeasFuncs && nMeasure < nMeasCount, "bumm" ); |
| return pMeasFuncs[nMeasure]; |
| } |
| |
| const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(long nMeasure) const |
| { |
| DBG_ASSERT( pMeasRefs && nMeasure < nMeasCount, "bumm" ); |
| return pMeasRefs[nMeasure]; |
| } |
| |
| sal_uInt16 ScDPResultData::GetMeasureRefOrient(long nMeasure) const |
| { |
| DBG_ASSERT( pMeasRefOrient && nMeasure < nMeasCount, "bumm" ); |
| return pMeasRefOrient[nMeasure]; |
| } |
| |
| String ScDPResultData::GetMeasureString(long nMeasure, sal_Bool bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const |
| { |
| // with bForce==sal_True, return function instead of "result" for single measure |
| // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc |
| rbTotalResult = false; |
| if ( nMeasure < 0 || ( nMeasCount == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE ) ) |
| { |
| // for user-specified subtotal function with all measures, |
| // display only function name |
| if ( eForceFunc != SUBTOTAL_FUNC_NONE ) |
| return ScGlobal::GetRscString(nFuncStrIds[eForceFunc]); |
| |
| rbTotalResult = true; |
| return ScGlobal::GetRscString(STR_TABLE_ERGEBNIS); |
| } |
| else |
| { |
| DBG_ASSERT( pMeasNames && nMeasure < nMeasCount, "bumm" ); |
| ScDPDimension* pDataDim = pSource->GetDataDimension(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; i<nMembCount; i++ ) |
| { |
| long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; |
| ScDPMember* pMember = pMembers->getByIndex(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<SCROW>::const_iterator itr = aMembers.begin(); |
| vector<SCROW> aChildMembers(++itr, aMembers.end()); |
| return pChildDim->IsValidEntry(aChildMembers); |
| } |
| else |
| return true; |
| } |
| |
| void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& 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<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& 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<sheet::GeneralFunction> 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<ScDPValueData>& 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<nUserSubCount; nUserPos++) // including hidden "automatic" |
| { |
| // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc |
| if ( pChildDimension && nUserSubCount > 1 ) |
| { |
| aSubState.nRowSubTotalFunc = nUserPos; |
| aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos ); |
| } |
| |
| pDataRoot->ProcessData( aDataMembers, aValues, aSubState ); |
| } |
| } |
| |
| /** |
| * Parse subtotal string and replace all occurrences of '?' with the caption |
| * string. Do ensure that escaped characters are not translated. |
| */ |
| static String lcl_parseSubtotalName(const String& rSubStr, const String& rCaption) |
| { |
| String aNewStr; |
| xub_StrLen n = rSubStr.Len(); |
| bool bEscaped = false; |
| for (xub_StrLen i = 0; i < n; ++i) |
| { |
| sal_Unicode c = rSubStr.GetChar(i); |
| if (!bEscaped && c == sal_Unicode('\\')) |
| { |
| bEscaped = true; |
| continue; |
| } |
| |
| if (!bEscaped && c == sal_Unicode('?')) |
| aNewStr.Append(rCaption); |
| else |
| aNewStr.Append(c); |
| bEscaped = false; |
| } |
| return aNewStr; |
| } |
| |
| void ScDPResultMember::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences, |
| long& rPos, long nMeasure, sal_Bool bRoot, |
| const String* pMemberName, |
| const String* pMemberCaption ) |
| { |
| // IsVisible() test is in ScDPResultDimension::FillMemberResults |
| // (not on data layout dimension) |
| |
| long nSize = GetSize(nMeasure); |
| sheet::MemberResult* pArray = pSequences->getArray(); |
| DBG_ASSERT( rPos+nSize <= pSequences->getLength(), "bumm" ); |
| |
| sal_Bool bIsNumeric = sal_False; |
| String aName; |
| if ( pMemberName ) // if pMemberName != NULL, use instead of real member name |
| aName = *pMemberName; |
| else |
| { |
| ScDPItemData aItemData; |
| FillItemData( aItemData ); |
| aName = aItemData.GetString(); |
| bIsNumeric = aItemData.IsValue(); |
| } |
| const ScDPDimension* pParentDim = GetParentDim(); |
| if ( bIsNumeric && pParentDim && pResultData->IsNumOrDateGroup( pParentDim->GetDimension() ) ) |
| { |
| // Numeric group dimensions use numeric entries for proper sorting, |
| // but the group titles must be output as text. |
| bIsNumeric = sal_False; |
| } |
| |
| String aCaption = aName; |
| const ScDPMember* pMemberDesc = GetDPMember(); |
| if (pMemberDesc) |
| { |
| const OUString* pLayoutName = pMemberDesc->GetLayoutName(); |
| if (pLayoutName) |
| { |
| aCaption = *pLayoutName; |
| bIsNumeric = false; // layout name is always non-numeric. |
| } |
| } |
| |
| if ( pMemberCaption ) // use pMemberCaption if != NULL |
| aCaption = *pMemberCaption; |
| if (!aCaption.Len()) |
| aCaption = ScGlobal::GetRscString(STR_EMPTYDATA); |
| |
| if (bIsNumeric) |
| pArray[rPos].Flags |= sheet::MemberResultFlags::NUMERIC; |
| else |
| pArray[rPos].Flags &= ~sheet::MemberResultFlags::NUMERIC; |
| |
| if ( nSize && !bRoot ) // root is overwritten by first dimension |
| { |
| pArray[rPos].Name = rtl::OUString(aName); |
| pArray[rPos].Caption = rtl::OUString(aCaption); |
| pArray[rPos].Flags |= sheet::MemberResultFlags::HASMEMBER; |
| |
| // set "continue" flag (removed for subtotals later) |
| for (long i=1; i<nSize; i++) |
| pArray[rPos+i].Flags |= sheet::MemberResultFlags::CONTINUE; |
| } |
| |
| const ScDPLevel* pParentLevel = GetParentLevel(); |
| long nExtraSpace = 0; |
| if ( pParentLevel && pParentLevel->IsAddEmpty() ) |
| ++nExtraSpace; |
| |
| sal_Bool bTitleLine = sal_False; |
| if ( pParentLevel && pParentLevel->IsOutlineLayout() ) |
| bTitleLine = sal_True; |
| |
| // if the subtotals are shown at the top (title row) in outline layout, |
| // no extra row for the subtotals is needed |
| sal_Bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure ); |
| |
| sal_Bool bHasChild = ( pChildDimension != NULL ); |
| if (bHasChild) |
| { |
| if ( bTitleLine ) // in tabular layout the title is on a separate row |
| ++rPos; // -> fill child dimension one row below |
| |
| if (bRoot) // same sequence for root member |
| pChildDimension->FillMemberResults( pSequences, rPos, nMeasure ); |
| else |
| //pChildDimension->FillMemberResults( pSequences + 1, rPos, nMeasure ); |
| pChildDimension->FillMemberResults( pSequences + nMemberStep/*1*/, rPos, nMeasure ); |
| |
| if ( bTitleLine ) // title row is included in GetSize, so the following |
| --rPos; // positions are calculated with the normal values |
| } |
| |
| rPos += nSize; |
| |
| long nUserSubStart; |
| long nUserSubCount = GetSubTotalCount(&nUserSubStart); |
| if ( nUserSubCount && pChildDimension && !bSubTotalInTitle ) |
| { |
| long nMemberMeasure = nMeasure; |
| long nSubSize = pResultData->GetCountForMeasure(nMeasure); |
| |
| rPos -= nSubSize * (nUserSubCount - nUserSubStart); // GetSize includes space for SubTotal |
| rPos -= nExtraSpace; // GetSize includes the empty line |
| |
| for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++) |
| { |
| for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) |
| { |
| if ( nMeasure == SC_DPMEASURE_ALL ) |
| nMemberMeasure = nSubCount; |
| |
| ScSubTotalFunc eForce = SUBTOTAL_FUNC_NONE; |
| if (bHasChild) |
| eForce = lcl_GetForceFunc( pParentLevel, nUserPos ); |
| |
| bool bTotalResult = false; |
| String aSubStr = aCaption; |
| aSubStr += ' '; |
| aSubStr += pResultData->GetMeasureString(nMemberMeasure, sal_False, eForce, bTotalResult); |
| |
| if (bTotalResult) |
| { |
| if (pMemberDesc) |
| { |
| // single data field layout. |
| const OUString* pSubtotalName = pParentDim->GetSubtotalName(); |
| if (pSubtotalName) |
| aSubStr = lcl_parseSubtotalName(*pSubtotalName, aCaption); |
| pArray[rPos].Flags &= ~sheet::MemberResultFlags::GRANDTOTAL; |
| } |
| else |
| { |
| // root member - subtotal (grand total?) for multi-data field layout. |
| const rtl::OUString* pGrandTotalName = pResultData->GetSource()->GetGrandTotalName(); |
| if (pGrandTotalName) |
| aSubStr = *pGrandTotalName; |
| pArray[rPos].Flags |= sheet::MemberResultFlags::GRANDTOTAL; |
| } |
| } |
| |
| pArray[rPos].Name = rtl::OUString(aName); |
| pArray[rPos].Caption = rtl::OUString(aSubStr); |
| pArray[rPos].Flags = ( pArray[rPos].Flags | |
| ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL) ) & |
| ~sheet::MemberResultFlags::CONTINUE; |
| |
| if ( nMeasure == SC_DPMEASURE_ALL ) |
| { |
| // data layout dimension is (direct/indirect) child of this. |
| // data layout dimension must have name for all entries. |
| |
| uno::Sequence<sheet::MemberResult>* pLayoutSeq = pSequences; |
| if (!bRoot) |
| ++pLayoutSeq; |
| ScDPResultDimension* pLayoutDim = pChildDimension; |
| while ( pLayoutDim && !pLayoutDim->IsDataLayout() ) |
| { |
| pLayoutDim = pLayoutDim->GetFirstChildDimension(); |
| ++pLayoutSeq; |
| } |
| if ( pLayoutDim ) |
| { |
| sheet::MemberResult* pLayoutArray = pLayoutSeq->getArray(); |
| String aDataName = pResultData->GetMeasureDimensionName(nMemberMeasure); |
| pLayoutArray[rPos].Name = rtl::OUString(aDataName); |
| } |
| } |
| |
| rPos += 1; |
| } |
| } |
| |
| rPos += nExtraSpace; // add again (subtracted above) |
| } |
| } |
| |
| void ScDPResultMember::FillDataResults( const ScDPResultMember* pRefMember, |
| uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, |
| long& rRow, long nMeasure ) const |
| { |
| // IsVisible() test is in ScDPResultDimension::FillDataResults |
| // (not on data layout dimension) |
| const ScDPLevel* pParentLevel = GetParentLevel(); |
| long nStartRow = rRow; |
| |
| long nExtraSpace = 0; |
| if ( pParentLevel && pParentLevel->IsAddEmpty() ) |
| ++nExtraSpace; |
| |
| sal_Bool bTitleLine = sal_False; |
| if ( pParentLevel && pParentLevel->IsOutlineLayout() ) |
| bTitleLine = sal_True; |
| |
| sal_Bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure ); |
| |
| sal_Bool bHasChild = ( pChildDimension != NULL ); |
| if (bHasChild) |
| { |
| if ( bTitleLine ) // in tabular layout the title is on a separate row |
| ++rRow; // -> fill child dimension one row below |
| |
| pChildDimension->FillDataResults( pRefMember, rSequence, rRow, nMeasure ); // doesn't modify rRow |
| rRow += GetSize( nMeasure ); |
| |
| if ( bTitleLine ) // title row is included in GetSize, so the following |
| --rRow; // positions are calculated with the normal values |
| } |
| |
| long nUserSubStart; |
| long nUserSubCount = GetSubTotalCount(&nUserSubStart); |
| if ( nUserSubCount || !bHasChild ) |
| { |
| // Calculate at least automatic if no subtotals are selected, |
| // show only own values if there's no child dimension (innermost). |
| if ( !nUserSubCount || !bHasChild ) |
| { |
| nUserSubCount = 1; |
| nUserSubStart = 0; |
| } |
| |
| long nMemberMeasure = nMeasure; |
| long nSubSize = pResultData->GetCountForMeasure(nMeasure); |
| if (bHasChild) |
| { |
| rRow -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal |
| rRow -= nExtraSpace; // GetSize includes the empty line |
| } |
| |
| long nMoveSubTotal = 0; |
| if ( bSubTotalInTitle ) |
| { |
| nMoveSubTotal = rRow - nStartRow; // force to first (title) row |
| rRow = nStartRow; |
| } |
| |
| if ( pDataRoot ) |
| { |
| ScDPSubTotalState aSubState; // initial state |
| |
| for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++) |
| { |
| if ( bHasChild && nUserSubCount > 1 ) |
| { |
| aSubState.nRowSubTotalFunc = nUserPos; |
| aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos ); |
| } |
| |
| for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) |
| { |
| if ( nMeasure == SC_DPMEASURE_ALL ) |
| nMemberMeasure = nSubCount; |
| else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL ) |
| nMemberMeasure = SC_DPMEASURE_ALL; |
| |
| DBG_ASSERT( rRow < rSequence.getLength(), "bumm" ); |
| uno::Sequence<sheet::DataResult>& rSubSeq = rSequence.getArray()[rRow]; |
| long nSeqCol = 0; |
| pDataRoot->FillDataRow( pRefMember, rSubSeq, nSeqCol, nMemberMeasure, bHasChild, aSubState ); |
| |
| rRow += 1; |
| } |
| } |
| } |
| else |
| rRow += nSubSize * ( nUserSubCount - nUserSubStart ); // empty rows occur when ShowEmpty is true |
| |
| // add extra space again if subtracted from GetSize above, |
| // add to own size if no children |
| rRow += nExtraSpace; |
| |
| rRow += nMoveSubTotal; |
| } |
| } |
| |
| void ScDPResultMember::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const |
| { |
| // IsVisible() test is in ScDPResultDimension::FillDataResults |
| // (not on data layout dimension) |
| |
| sal_Bool bHasChild = ( pChildDimension != NULL ); |
| |
| long nUserSubCount = GetSubTotalCount(); |
| // process subtotals even if not shown |
| // if ( nUserSubCount || !bHasChild ) |
| { |
| // Calculate at least automatic if no subtotals are selected, |
| // show only own values if there's no child dimension (innermost). |
| if ( !nUserSubCount || !bHasChild ) |
| nUserSubCount = 1; |
| |
| long nMemberMeasure = nMeasure; |
| long nSubSize = pResultData->GetCountForMeasure(nMeasure); |
| |
| if ( pDataRoot ) |
| { |
| ScDPSubTotalState aSubState; // initial state |
| |
| for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" |
| { |
| if ( bHasChild && nUserSubCount > 1 ) |
| { |
| aSubState.nRowSubTotalFunc = nUserPos; |
| aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos ); |
| } |
| |
| for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) |
| { |
| if ( nMeasure == SC_DPMEASURE_ALL ) |
| nMemberMeasure = nSubCount; |
| else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL ) |
| nMemberMeasure = SC_DPMEASURE_ALL; |
| |
| pDataRoot->UpdateDataRow( pRefMember, nMemberMeasure, bHasChild, aSubState ); |
| } |
| } |
| } |
| } |
| |
| if (bHasChild) // child dimension must be processed last, so the column total is known |
| { |
| pChildDimension->UpdateDataResults( pRefMember, nMeasure ); |
| } |
| } |
| |
| void ScDPResultMember::SortMembers( ScDPResultMember* pRefMember ) |
| { |
| sal_Bool bHasChild = ( pChildDimension != NULL ); |
| if (bHasChild) |
| pChildDimension->SortMembers( pRefMember ); // sorting is done at the dimension |
| |
| if ( IsRoot() && pDataRoot ) |
| { |
| // use the row root member to sort columns |
| // sub total count is always 1 |
| |
| pDataRoot->SortMembers( pRefMember ); |
| } |
| } |
| |
| void ScDPResultMember::DoAutoShow( ScDPResultMember* pRefMember ) |
| { |
| sal_Bool bHasChild = ( pChildDimension != NULL ); |
| if (bHasChild) |
| pChildDimension->DoAutoShow( pRefMember ); // sorting is done at the dimension |
| |
| if ( IsRoot()&& pDataRoot ) |
| { |
| // use the row root member to sort columns |
| // sub total count is always 1 |
| |
| pDataRoot->DoAutoShow( pRefMember ); |
| } |
| } |
| |
| void ScDPResultMember::ResetResults( sal_Bool /*bRoot*/ ) |
| { |
| if (pDataRoot) |
| pDataRoot->ResetResults(); |
| |
| if (pChildDimension) |
| pChildDimension->ResetResults(); |
| |
| // if (!bRoot) |
| // bHasElements = sal_False; |
| } |
| |
| void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure, |
| ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const |
| { |
| // IsVisible() test is in ScDPResultDimension::FillDataResults |
| // (not on data layout dimension) |
| |
| rTotals.SetInColRoot( IsRoot() ); |
| |
| sal_Bool bHasChild = ( pChildDimension != NULL ); |
| |
| long nUserSubCount = GetSubTotalCount(); |
| //if ( nUserSubCount || !bHasChild ) |
| { |
| // Calculate at least automatic if no subtotals are selected, |
| // show only own values if there's no child dimension (innermost). |
| if ( !nUserSubCount || !bHasChild ) |
| nUserSubCount = 1; |
| |
| long nMemberMeasure = nMeasure; |
| long nSubSize = pResultData->GetCountForMeasure(nMeasure); |
| |
| if ( pDataRoot ) |
| { |
| ScDPSubTotalState aSubState; // initial state |
| |
| for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" |
| { |
| if ( bHasChild && nUserSubCount > 1 ) |
| { |
| aSubState.nRowSubTotalFunc = nUserPos; |
| aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel(), nUserPos ); |
| } |
| |
| for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) |
| { |
| if ( nMeasure == SC_DPMEASURE_ALL ) |
| nMemberMeasure = nSubCount; |
| else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL ) |
| nMemberMeasure = SC_DPMEASURE_ALL; |
| |
| pDataRoot->UpdateRunningTotals( pRefMember, nMemberMeasure, |
| bHasChild, aSubState, rRunning, rTotals, *this ); |
| } |
| } |
| } |
| } |
| |
| if (bHasChild) // child dimension must be processed last, so the column total is known |
| { |
| pChildDimension->UpdateRunningTotals( pRefMember, nMeasure, rRunning, rTotals ); |
| } |
| } |
| |
| void ScDPResultMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const |
| { |
| lcl_DumpRow( String::CreateFromAscii("ScDPResultMember"), GetName(), NULL, pDoc, rPos ); |
| SCROW nStartRow = rPos.Row(); |
| |
| if (pDataRoot) |
| pDataRoot->DumpState( pRefMember, pDoc, rPos ); |
| |
| if (pChildDimension) |
| pChildDimension->DumpState( pRefMember, pDoc, rPos ); |
| |
| lcl_Indent( pDoc, nStartRow, rPos ); |
| } |
| |
| ScDPAggData* ScDPResultMember::GetColTotal( long nMeasure ) const |
| { |
| return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure ); |
| } |
| |
| void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData& rData) const |
| { |
| if (pChildDimension) |
| pChildDimension->FillVisibilityData(rData); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| ScDPDataMember::ScDPDataMember( const ScDPResultData* pData, const ScDPResultMember* pRes ) : |
| pResultData( pData ), |
| pResultMember( pRes ), |
| pChildDimension( NULL ) |
| { |
| // pResultMember is 0 for root members |
| } |
| |
| ScDPDataMember::~ScDPDataMember() |
| { |
| delete pChildDimension; |
| } |
| |
| String ScDPDataMember::GetName() const |
| { |
| if (pResultMember) |
| return pResultMember->GetName(); |
| else |
| return EMPTY_STRING; |
| } |
| |
| sal_Bool ScDPDataMember::IsVisible() const |
| { |
| if (pResultMember) |
| return pResultMember->IsVisible(); |
| else |
| return sal_False; |
| } |
| |
| sal_Bool ScDPDataMember::IsNamedItem( /*const ScDPItemData& r*/SCROW r ) const |
| { |
| if (pResultMember) |
| return pResultMember->IsNamedItem(r); |
| else |
| return sal_False; |
| } |
| |
| sal_Bool ScDPDataMember::HasHiddenDetails() const |
| { |
| if (pResultMember) |
| return pResultMember->HasHiddenDetails(); |
| else |
| return sal_False; |
| } |
| |
| void ScDPDataMember::InitFrom( const ScDPResultDimension* pDim ) |
| { |
| if ( !pChildDimension ) |
| pChildDimension = new ScDPDataDimension(pResultData); |
| pChildDimension->InitFrom(pDim); |
| } |
| |
| const long SC_SUBTOTALPOS_AUTO = -1; // default |
| const long SC_SUBTOTALPOS_SKIP = -2; // don't use |
| |
| long lcl_GetSubTotalPos( const ScDPSubTotalState& rSubState ) |
| { |
| if ( rSubState.nColSubTotalFunc >= 0 && rSubState.nRowSubTotalFunc >= 0 && |
| rSubState.nColSubTotalFunc != rSubState.nRowSubTotalFunc ) |
| { |
| // #i68338# don't return the same index for different combinations (leading to repeated updates), |
| // return a "don't use" value instead |
| |
| return SC_SUBTOTALPOS_SKIP; |
| } |
| |
| long nRet = SC_SUBTOTALPOS_AUTO; |
| if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc; |
| if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc; |
| return nRet; |
| } |
| |
| void ScDPDataMember::UpdateValues( const vector<ScDPValueData>& aValues, const ScDPSubTotalState& rSubState ) |
| { |
| //! find out how many and which subtotals are used |
| |
| ScDPAggData* pAgg = &aAggregate; |
| |
| long nSubPos = lcl_GetSubTotalPos(rSubState); |
| if (nSubPos == SC_SUBTOTALPOS_SKIP) |
| return; |
| if (nSubPos > 0) |
| { |
| long nSkip = nSubPos * pResultData->GetMeasureCount(); |
| for (long i=0; i<nSkip; i++) |
| pAgg = pAgg->GetChild(); // created if not there |
| } |
| |
| size_t nCount = aValues.size(); |
| for (size_t nPos = 0; nPos < nCount; ++nPos) |
| { |
| pAgg->Update(aValues[nPos], pResultData->GetMeasureFunction(nPos), rSubState); |
| pAgg = pAgg->GetChild(); |
| } |
| } |
| |
| void ScDPDataMember::ProcessData( const vector< SCROW >& aChildMembers, const vector<ScDPValueData>& aValues, |
| const ScDPSubTotalState& rSubState ) |
| { |
| if ( pResultData->IsLateInit() && !pChildDimension && pResultMember && pResultMember->GetChildDimension() ) |
| { |
| // if this DataMember doesn't have a child dimension because the ResultMember's |
| // child dimension wasn't there yet during this DataMembers's creation, |
| // create the child dimension now |
| InitFrom( pResultMember->GetChildDimension() ); |
| } |
| |
| ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column |
| |
| long nUserSubCount = pResultMember ? pResultMember->GetSubTotalCount() : 0; |
| |
| // 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; |
| |
| for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" |
| { |
| if ( pChildDimension && nUserSubCount > 1 ) |
| { |
| const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; |
| aLocalSubState.nColSubTotalFunc = nUserPos; |
| aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); |
| } |
| |
| UpdateValues( aValues, aLocalSubState ); |
| } |
| |
| if (pChildDimension) |
| pChildDimension->ProcessData( aChildMembers, aValues, rSubState ); // with unmodified subtotal state |
| } |
| |
| sal_Bool ScDPDataMember::HasData( long nMeasure, const ScDPSubTotalState& rSubState ) const |
| { |
| if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE && |
| rSubState.eColForce != rSubState.eRowForce ) |
| return sal_False; |
| |
| // #74542# HasData can be different between measures! |
| |
| const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState ); |
| if (!pAgg) |
| return sal_False; //! error? |
| |
| return pAgg->HasData(); |
| } |
| |
| sal_Bool ScDPDataMember::HasError( long nMeasure, const ScDPSubTotalState& rSubState ) const |
| { |
| const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState ); |
| if (!pAgg) |
| return sal_True; |
| |
| return pAgg->HasError(); |
| } |
| |
| double ScDPDataMember::GetAggregate( long nMeasure, const ScDPSubTotalState& rSubState ) const |
| { |
| const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState ); |
| if (!pAgg) |
| return DBL_MAX; //! error? |
| |
| return pAgg->GetResult(); |
| } |
| |
| ScDPAggData* ScDPDataMember::GetAggData( long nMeasure, const ScDPSubTotalState& rSubState ) |
| { |
| DBG_ASSERT( nMeasure >= 0, "GetAggData: no measure" ); |
| |
| ScDPAggData* pAgg = &aAggregate; |
| long nSkip = nMeasure; |
| long nSubPos = lcl_GetSubTotalPos(rSubState); |
| if (nSubPos == SC_SUBTOTALPOS_SKIP) |
| return NULL; |
| if (nSubPos > 0) |
| nSkip += nSubPos * pResultData->GetMeasureCount(); |
| |
| for ( long nPos=0; nPos<nSkip; nPos++ ) |
| pAgg = pAgg->GetChild(); //! need to create children here? |
| |
| return pAgg; |
| } |
| |
| const ScDPAggData* ScDPDataMember::GetConstAggData( long nMeasure, const ScDPSubTotalState& rSubState ) const |
| { |
| DBG_ASSERT( nMeasure >= 0, "GetConstAggData: no measure" ); |
| |
| const ScDPAggData* pAgg = &aAggregate; |
| long nSkip = nMeasure; |
| long nSubPos = lcl_GetSubTotalPos(rSubState); |
| if (nSubPos == SC_SUBTOTALPOS_SKIP) |
| return NULL; |
| if (nSubPos > 0) |
| nSkip += nSubPos * pResultData->GetMeasureCount(); |
| |
| for ( long nPos=0; nPos<nSkip; nPos++ ) |
| { |
| pAgg = pAgg->GetExistingChild(); |
| if (!pAgg) |
| return NULL; |
| } |
| |
| return pAgg; |
| } |
| |
| void ScDPDataMember::FillDataRow( const ScDPResultMember* pRefMember, |
| uno::Sequence<sheet::DataResult>& rSequence, |
| long& rCol, long nMeasure, sal_Bool bIsSubTotalRow, |
| const ScDPSubTotalState& rSubState ) const |
| { |
| DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); |
| |
| if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension::FillDataRow ??? |
| { |
| long nStartCol = rCol; |
| |
| const ScDPDataDimension* pDataChild = GetChildDimension(); |
| const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); |
| |
| const ScDPLevel* pRefParentLevel = const_cast<ScDPResultMember*>(pRefMember)->GetParentLevel(); |
| |
| long nExtraSpace = 0; |
| if ( pRefParentLevel && pRefParentLevel->IsAddEmpty() ) |
| ++nExtraSpace; |
| |
| sal_Bool bTitleLine = sal_False; |
| if ( pRefParentLevel && pRefParentLevel->IsOutlineLayout() ) |
| bTitleLine = sal_True; |
| |
| sal_Bool bSubTotalInTitle = pRefMember->IsSubTotalInTitle( nMeasure ); |
| |
| // leave space for children even if the DataMember hasn't been initialized |
| // (pDataChild is null then, this happens when no values for it are in this row) |
| sal_Bool bHasChild = ( pRefChild != NULL ); |
| |
| if ( bHasChild ) |
| { |
| if ( bTitleLine ) // in tabular layout the title is on a separate column |
| ++rCol; // -> fill child dimension one column below |
| |
| if ( pDataChild ) |
| pDataChild->FillDataRow( pRefChild, rSequence, rCol, nMeasure, bIsSubTotalRow, rSubState ); |
| rCol += (sal_uInt16)pRefMember->GetSize( nMeasure ); |
| |
| if ( bTitleLine ) // title column is included in GetSize, so the following |
| --rCol; // positions are calculated with the normal values |
| } |
| |
| long nUserSubStart; |
| long nUserSubCount = pRefMember->GetSubTotalCount(&nUserSubStart); |
| if ( nUserSubCount || !bHasChild ) |
| { |
| // Calculate at least automatic if no subtotals are selected, |
| // show only own values if there's no child dimension (innermost). |
| if ( !nUserSubCount || !bHasChild ) |
| { |
| nUserSubCount = 1; |
| nUserSubStart = 0; |
| } |
| |
| ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column |
| |
| long nMemberMeasure = nMeasure; |
| long nSubSize = pResultData->GetCountForMeasure(nMeasure); |
| if (bHasChild) |
| { |
| rCol -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal |
| rCol -= nExtraSpace; // GetSize includes the empty line |
| } |
| |
| long nMoveSubTotal = 0; |
| if ( bSubTotalInTitle ) |
| { |
| nMoveSubTotal = rCol - nStartCol; // force to first (title) column |
| rCol = nStartCol; |
| } |
| |
| for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++) |
| { |
| if ( pChildDimension && nUserSubCount > 1 ) |
| { |
| const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; |
| aLocalSubState.nColSubTotalFunc = nUserPos; |
| aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); |
| } |
| |
| for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) |
| { |
| if ( nMeasure == SC_DPMEASURE_ALL ) |
| nMemberMeasure = nSubCount; |
| |
| DBG_ASSERT( rCol < rSequence.getLength(), "bumm" ); |
| sheet::DataResult& rRes = rSequence.getArray()[rCol]; |
| |
| if ( HasData( nMemberMeasure, aLocalSubState ) ) |
| { |
| if ( HasError( nMemberMeasure, aLocalSubState ) ) |
| { |
| rRes.Value = 0; |
| rRes.Flags |= sheet::DataResultFlags::ERROR; |
| } |
| else |
| { |
| rRes.Value = GetAggregate( nMemberMeasure, aLocalSubState ); |
| rRes.Flags |= sheet::DataResultFlags::HASDATA; |
| } |
| } |
| |
| if ( bHasChild || bIsSubTotalRow ) |
| rRes.Flags |= sheet::DataResultFlags::SUBTOTAL; |
| |
| rCol += 1; |
| } |
| } |
| |
| // add extra space again if subtracted from GetSize above, |
| // add to own size if no children |
| rCol += nExtraSpace; |
| |
| rCol += nMoveSubTotal; |
| } |
| } |
| } |
| |
| void ScDPDataMember::UpdateDataRow( const ScDPResultMember* pRefMember, |
| long nMeasure, sal_Bool bIsSubTotalRow, |
| const ScDPSubTotalState& rSubState ) |
| { |
| DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); |
| |
| // Calculate must be called even if not visible (for use as reference value) |
| const ScDPDataDimension* pDataChild = GetChildDimension(); |
| const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); |
| |
| // leave space for children even if the DataMember hasn't been initialized |
| // (pDataChild is null then, this happens when no values for it are in this row) |
| sal_Bool bHasChild = ( pRefChild != NULL ); |
| |
| // process subtotals even if not shown |
| long nUserSubCount = pRefMember->GetSubTotalCount(); |
| |
| // Calculate at least automatic if no subtotals are selected, |
| // show only own values if there's no child dimension (innermost). |
| if ( !nUserSubCount || !bHasChild ) |
| nUserSubCount = 1; |
| |
| ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column |
| |
| long nMemberMeasure = nMeasure; |
| long nSubSize = pResultData->GetCountForMeasure(nMeasure); |
| |
| for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" |
| { |
| if ( pChildDimension && nUserSubCount > 1 ) |
| { |
| const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; |
| aLocalSubState.nColSubTotalFunc = nUserPos; |
| aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); |
| } |
| |
| for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) |
| { |
| if ( nMeasure == SC_DPMEASURE_ALL ) |
| nMemberMeasure = nSubCount; |
| |
| // update data... |
| ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState ); |
| if (pAggData) |
| { |
| //! aLocalSubState? |
| ScSubTotalFunc eFunc = pResultData->GetMeasureFunction( nMemberMeasure ); |
| sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure ); |
| sal_Int32 eRefType = aReferenceValue.ReferenceType; |
| |
| // calculate the result first - for all members, regardless of reference value |
| pAggData->Calculate( eFunc, aLocalSubState ); |
| |
| if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE || |
| eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE || |
| eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ) |
| { |
| // copy the result into auxiliary value, so differences can be |
| // calculated in any order |
| pAggData->SetAuxiliary( pAggData->GetResult() ); |
| } |
| // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting |
| } |
| } |
| } |
| |
| if ( bHasChild ) // child dimension must be processed last, so the row total is known |
| { |
| if ( pDataChild ) |
| pDataChild->UpdateDataRow( pRefChild, nMeasure, bIsSubTotalRow, rSubState ); |
| } |
| } |
| |
| void ScDPDataMember::SortMembers( ScDPResultMember* pRefMember ) |
| { |
| DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); |
| |
| if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ??? |
| { |
| ScDPDataDimension* pDataChild = GetChildDimension(); |
| ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); |
| if ( pRefChild && pDataChild ) |
| pDataChild->SortMembers( pRefChild ); // sorting is done at the dimension |
| } |
| } |
| |
| void ScDPDataMember::DoAutoShow( ScDPResultMember* pRefMember ) |
| { |
| DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); |
| |
| if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ??? |
| { |
| ScDPDataDimension* pDataChild = GetChildDimension(); |
| ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); |
| if ( pRefChild && pDataChild ) |
| pDataChild->DoAutoShow( pRefChild ); // sorting is done at the dimension |
| } |
| } |
| |
| void ScDPDataMember::ResetResults() |
| { |
| aAggregate.Reset(); |
| |
| ScDPDataDimension* pDataChild = GetChildDimension(); |
| if ( pDataChild ) |
| pDataChild->ResetResults(); |
| } |
| |
| void ScDPDataMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, |
| long nMeasure, sal_Bool bIsSubTotalRow, |
| const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning, |
| ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) |
| { |
| DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); |
| |
| if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension::UpdateRunningTotals ??? |
| { |
| const ScDPDataDimension* pDataChild = GetChildDimension(); |
| const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); |
| |
| sal_Bool bIsRoot = ( pResultMember == NULL || pResultMember->GetParentLevel() == NULL ); |
| |
| // leave space for children even if the DataMember hasn't been initialized |
| // (pDataChild is null then, this happens when no values for it are in this row) |
| sal_Bool bHasChild = ( pRefChild != NULL ); |
| |
| long nUserSubCount = pRefMember->GetSubTotalCount(); |
| //if ( nUserSubCount || !bHasChild ) |
| { |
| // Calculate at least automatic if no subtotals are selected, |
| // show only own values if there's no child dimension (innermost). |
| if ( !nUserSubCount || !bHasChild ) |
| nUserSubCount = 1; |
| |
| ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column |
| |
| long nMemberMeasure = nMeasure; |
| long nSubSize = pResultData->GetCountForMeasure(nMeasure); |
| |
| for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" |
| { |
| if ( pChildDimension && nUserSubCount > 1 ) |
| { |
| const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; |
| aLocalSubState.nColSubTotalFunc = nUserPos; |
| aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); |
| } |
| |
| for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) |
| { |
| if ( nMeasure == SC_DPMEASURE_ALL ) |
| nMemberMeasure = nSubCount; |
| |
| // update data... |
| ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState ); |
| if (pAggData) |
| { |
| //! aLocalSubState? |
| sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure ); |
| sal_Int32 eRefType = aReferenceValue.ReferenceType; |
| |
| if ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL || |
| eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE || |
| eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE || |
| eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ) |
| { |
| sal_Bool bRunningTotal = ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ); |
| sal_Bool bRelative = |
| ( aReferenceValue.ReferenceItemType != sheet::DataPilotFieldReferenceItemType::NAMED && !bRunningTotal ); |
| long nRelativeDir = bRelative ? |
| ( ( aReferenceValue.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::PREVIOUS ) ? -1 : 1 ) : 0; |
| |
| const long* pColVisible = rRunning.GetColVisible(); |
| const long* pColIndexes = rRunning.GetColIndexes(); |
| const long* pRowVisible = rRunning.GetRowVisible(); |
| const long* pRowIndexes = rRunning.GetRowIndexes(); |
| |
| String aRefFieldName = aReferenceValue.ReferenceField; |
| |
| //! aLocalSubState? |
| sal_uInt16 nRefOrient = pResultData->GetMeasureRefOrient( nMemberMeasure ); |
| sal_Bool bRefDimInCol = ( nRefOrient == sheet::DataPilotFieldOrientation_COLUMN ); |
| sal_Bool bRefDimInRow = ( nRefOrient == sheet::DataPilotFieldOrientation_ROW ); |
| |
| const ScDPResultDimension* pSelectDim = NULL; |
| long nRowPos = 0; |
| long nColPos = 0; |
| |
| // |
| // find the reference field in column or row dimensions |
| // |
| |
| if ( bRefDimInRow ) // look in row dimensions |
| { |
| pSelectDim = rRunning.GetRowResRoot()->GetChildDimension(); |
| while ( pSelectDim && pSelectDim->GetName() != aRefFieldName ) |
| { |
| long nIndex = pRowIndexes[nRowPos]; |
| if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() ) |
| pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension(); |
| else |
| pSelectDim = NULL; |
| ++nRowPos; |
| } |
| // child dimension of innermost member? |
| if ( pSelectDim && pRowIndexes[nRowPos] < 0 ) |
| pSelectDim = NULL; |
| } |
| |
| if ( bRefDimInCol ) // look in column dimensions |
| { |
| pSelectDim = rRunning.GetColResRoot()->GetChildDimension(); |
| while ( pSelectDim && pSelectDim->GetName() != aRefFieldName ) |
| { |
| long nIndex = pColIndexes[nColPos]; |
| if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() ) |
| pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension(); |
| else |
| pSelectDim = NULL; |
| ++nColPos; |
| } |
| // child dimension of innermost member? |
| if ( pSelectDim && pColIndexes[nColPos] < 0 ) |
| pSelectDim = NULL; |
| } |
| |
| sal_Bool bNoDetailsInRef = sal_False; |
| if ( pSelectDim && bRunningTotal ) |
| { |
| // Running totals: |
| // If details are hidden for this member in the reference dimension, |
| // don't show or sum up the value. Otherwise, for following members, |
| // the running totals of details and subtotals wouldn't match. |
| |
| long nMyIndex = bRefDimInCol ? pColIndexes[nColPos] : pRowIndexes[nRowPos]; |
| if ( nMyIndex >= 0 && nMyIndex < pSelectDim->GetMemberCount() ) |
| { |
| const ScDPResultMember* pMyRefMember = pSelectDim->GetMember(nMyIndex); |
| if ( pMyRefMember && pMyRefMember->HasHiddenDetails() ) |
| { |
| pSelectDim = NULL; // don't calculate |
| bNoDetailsInRef = sal_True; // show error, not empty |
| } |
| } |
| } |
| |
| if ( bRelative ) |
| { |
| // Difference/Percentage from previous/next: |
| // If details are hidden for this member in the innermost column/row |
| // dimension (the orientation of the reference dimension), show an |
| // error value. |
| // - If the no-details dimension is the reference dimension, its |
| // members will be skipped when finding the previous/next member, |
| // so there must be no results for its members. |
| // - If the no-details dimension is outside of the reference dimension, |
| // no calculation in the reference dimension is possible. |
| // - Otherwise, the error isn't strictly necessary, but shown for |
| // consistency. |
| |
| sal_Bool bInnerNoDetails = bRefDimInCol ? HasHiddenDetails() : |
| ( bRefDimInRow ? rRowParent.HasHiddenDetails() : sal_True ); |
| if ( bInnerNoDetails ) |
| { |
| pSelectDim = NULL; |
| bNoDetailsInRef = sal_True; // show error, not empty |
| } |
| } |
| |
| if ( !bRefDimInCol && !bRefDimInRow ) // invalid dimension specified |
| bNoDetailsInRef = sal_True; // pSelectDim is then already NULL |
| |
| // |
| // get the member for the reference item and do the calculation |
| // |
| |
| if ( bRunningTotal ) |
| { |
| // running total in (dimension) -> find first existing member |
| |
| if ( pSelectDim ) |
| { |
| ScDPDataMember* pSelectMember; |
| if ( bRefDimInCol ) |
| pSelectMember = ScDPResultDimension::GetColReferenceMember( NULL, NULL, |
| nColPos, rRunning ); |
| else |
| { |
| long nSkip = nRowPos + 1; // including the reference dimension |
| pSelectMember = pSelectDim->GetRowReferenceMember( NULL, NULL, |
| pRowIndexes+nSkip, pColIndexes ); |
| } |
| |
| if ( pSelectMember ) |
| { |
| // The running total is kept as the auxiliary value in |
| // the first available member for the reference dimension. |
| // Members are visited in final order, so each one's result |
| // can be used and then modified. |
| |
| ScDPAggData* pSelectData = pSelectMember-> |
| GetAggData( nMemberMeasure, aLocalSubState ); |
| if ( pSelectData ) |
| { |
| double fTotal = pSelectData->GetAuxiliary(); |
| fTotal += pAggData->GetResult(); |
| pSelectData->SetAuxiliary( fTotal ); |
| pAggData->SetResult( fTotal ); |
| pAggData->SetEmpty(sal_False); // always display |
| } |
| } |
| else |
| pAggData->SetError(); |
| } |
| else if (bNoDetailsInRef) |
| pAggData->SetError(); |
| else |
| pAggData->SetEmpty(sal_True); // empty (dim set to 0 above) |
| } |
| else |
| { |
| // difference/percentage -> find specified member |
| |
| if ( pSelectDim ) |
| { |
| String aRefItemName = aReferenceValue.ReferenceItemName; |
| ScDPRelativePos aRefItemPos( 0, nRelativeDir ); // nBasePos is modified later |
| |
| const String* pRefName = NULL; |
| const ScDPRelativePos* pRefPos = NULL; |
| if ( bRelative ) |
| pRefPos = &aRefItemPos; |
| else |
| pRefName = &aRefItemName; |
| |
| ScDPDataMember* pSelectMember; |
| if ( bRefDimInCol ) |
| { |
| aRefItemPos.nBasePos = pColVisible[nColPos]; // without sort order applied |
| pSelectMember = ScDPResultDimension::GetColReferenceMember( pRefPos, pRefName, |
| nColPos, rRunning ); |
| } |
| else |
| { |
| aRefItemPos.nBasePos = pRowVisible[nRowPos]; // without sort order applied |
| long nSkip = nRowPos + 1; // including the reference dimension |
| pSelectMember = pSelectDim->GetRowReferenceMember( pRefPos, pRefName, |
| pRowIndexes+nSkip, pColIndexes ); |
| } |
| |
| // difference or perc.difference is empty for the reference item itself |
| if ( pSelectMember == this && |
| eRefType != sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ) |
| { |
| pAggData->SetEmpty(sal_True); |
| } |
| else if ( pSelectMember ) |
| { |
| const ScDPAggData* pOtherAggData = pSelectMember-> |
| GetConstAggData( nMemberMeasure, aLocalSubState ); |
| DBG_ASSERT( pOtherAggData, "no agg data" ); |
| if ( pOtherAggData ) |
| { |
| // Reference member may be visited before or after this one, |
| // so the auxiliary value is used for the original result. |
| |
| double fOtherResult = pOtherAggData->GetAuxiliary(); |
| double fThisResult = pAggData->GetResult(); |
| sal_Bool bError = sal_False; |
| switch ( eRefType ) |
| { |
| case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE: |
| fThisResult = fThisResult - fOtherResult; |
| break; |
| case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE: |
| if ( fOtherResult == 0.0 ) |
| bError = sal_True; |
| else |
| fThisResult = fThisResult / fOtherResult; |
| break; |
| case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE: |
| if ( fOtherResult == 0.0 ) |
| bError = sal_True; |
| else |
| fThisResult = ( fThisResult - fOtherResult ) / fOtherResult; |
| break; |
| default: |
| DBG_ERROR("invalid calculation type"); |
| } |
| if ( bError ) |
| { |
| pAggData->SetError(); |
| } |
| else |
| { |
| pAggData->SetResult(fThisResult); |
| pAggData->SetEmpty(sal_False); // always display |
| } |
| //! errors in data? |
| } |
| } |
| else if (bRelative && !bNoDetailsInRef) |
| pAggData->SetEmpty(sal_True); // empty |
| else |
| pAggData->SetError(); // error |
| } |
| else if (bNoDetailsInRef) |
| pAggData->SetError(); // error |
| else |
| pAggData->SetEmpty(sal_True); // empty |
| } |
| } |
| else if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE || |
| eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE || |
| eRefType == sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE || |
| eRefType == sheet::DataPilotFieldReferenceType::INDEX ) |
| { |
| // |
| // set total values when they are encountered (always before their use) |
| // |
| |
| ScDPAggData* pColTotalData = pRefMember->GetColTotal( nMemberMeasure ); |
| ScDPAggData* pRowTotalData = rTotals.GetRowTotal( nMemberMeasure ); |
| ScDPAggData* pGrandTotalData = rTotals.GetGrandTotal( nMemberMeasure ); |
| |
| double fTotalValue = pAggData->HasError() ? 0 : pAggData->GetResult(); |
| |
| if ( bIsRoot && rTotals.IsInColRoot() && pGrandTotalData ) |
| pGrandTotalData->SetAuxiliary( fTotalValue ); |
| |
| if ( bIsRoot && pRowTotalData ) |
| pRowTotalData->SetAuxiliary( fTotalValue ); |
| |
| if ( rTotals.IsInColRoot() && pColTotalData ) |
| pColTotalData->SetAuxiliary( fTotalValue ); |
| |
| // |
| // find relation to total values |
| // |
| |
| switch ( eRefType ) |
| { |
| case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE: |
| case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE: |
| case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE: |
| { |
| double nTotal; |
| if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE ) |
| nTotal = pRowTotalData->GetAuxiliary(); |
| else if ( eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE ) |
| nTotal = pColTotalData->GetAuxiliary(); |
| else |
| nTotal = pGrandTotalData->GetAuxiliary(); |
| |
| if ( nTotal == 0.0 ) |
| pAggData->SetError(); |
| else |
| pAggData->SetResult( pAggData->GetResult() / nTotal ); |
| } |
| break; |
| case sheet::DataPilotFieldReferenceType::INDEX: |
| { |
| double nColTotal = pColTotalData->GetAuxiliary(); |
| double nRowTotal = pRowTotalData->GetAuxiliary(); |
| double nGrandTotal = pGrandTotalData->GetAuxiliary(); |
| if ( nRowTotal == 0.0 || nColTotal == 0.0 ) |
| pAggData->SetError(); |
| else |
| pAggData->SetResult( |
| ( pAggData->GetResult() * nGrandTotal ) / |
| ( nRowTotal * nColTotal ) ); |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if ( bHasChild ) // child dimension must be processed last, so the row total is known |
| { |
| if ( pDataChild ) |
| pDataChild->UpdateRunningTotals( pRefChild, nMeasure, |
| bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent ); |
| } |
| } |
| } |
| |
| void ScDPDataMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const |
| { |
| lcl_DumpRow( String::CreateFromAscii("ScDPDataMember"), GetName(), &aAggregate, pDoc, rPos ); |
| SCROW nStartRow = rPos.Row(); |
| |
| const ScDPDataDimension* pDataChild = GetChildDimension(); |
| const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); |
| if ( pDataChild && pRefChild ) |
| pDataChild->DumpState( pRefChild, pDoc, rPos ); |
| |
| lcl_Indent( pDoc, nStartRow, rPos ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| // Helper class to select the members to include in |
| // ScDPResultDimension::InitFrom or LateInitFrom if groups are used |
| |
| class ScDPGroupCompare |
| { |
| private: |
| const ScDPResultData* pResultData; |
| const ScDPInitState& rInitState; |
| long nDimSource; |
| sal_Bool bIncludeAll; |
| sal_Bool bIsBase; |
| long nGroupBase; |
| // Wang Xu Ming -- 2009-8-6 |
| // DataPilot Migration - Cache&&Performance |
| SCROW nBaseDataId; |
| // End Comments |
| public: |
| ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ); |
| ~ScDPGroupCompare() {} |
| |
| sal_Bool IsIncluded( const ScDPMember& rMember ) { return bIncludeAll || TestIncluded( rMember ); } |
| sal_Bool TestIncluded( const ScDPMember& rMember ); |
| }; |
| |
| ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ) : |
| pResultData( pData ), |
| rInitState( rState ), |
| nDimSource( nDimension ), |
| nBaseDataId( -1 ) |
| { |
| bIsBase = pResultData->IsBaseForGroup( nDimSource ); |
| nGroupBase = pResultData->GetGroupBase( nDimSource ); //! get together in one call? |
| if ( nGroupBase >= 0 ) |
| nBaseDataId = rInitState.GetNameIdForIndex( nGroupBase ); |
| |
| // if bIncludeAll is set, TestIncluded doesn't need to be called |
| bIncludeAll = !( bIsBase || nGroupBase >= 0 ); |
| } |
| |
| sal_Bool ScDPGroupCompare::TestIncluded( const ScDPMember& rMember ) |
| { |
| sal_Bool bInclude = sal_True; |
| if ( nBaseDataId >=0 ) |
| { |
| ScDPItemData aMemberData; |
| rMember.FillItemData( aMemberData ); |
| bInclude = pResultData->IsInGroup( aMemberData, nDimSource, nBaseDataId, nGroupBase ); |
| } |
| else if ( bIsBase ) |
| { |
| // need to check all previous groups |
| //! get array of groups (or indexes) before loop? |
| ScDPItemData aMemberData; |
| rMember.FillItemData( aMemberData ); |
| long nInitCount = rInitState.GetCount(); |
| const long* pInitSource = rInitState.GetSource(); |
| /*const ScDPItemData* pInitNames = rInitState.GetNames();*/ |
| const SCROW* pInitNames = rInitState.GetNameIds(); |
| for (long nInitPos=0; nInitPos<nInitCount && bInclude; nInitPos++) |
| if ( pResultData->GetGroupBase( pInitSource[nInitPos] ) == nDimSource ) |
| { |
| bInclude = pResultData->IsInGroup( pInitNames[nInitPos], pInitSource[nInitPos], |
| aMemberData, nDimSource ); |
| } |
| } |
| else if ( nGroupBase >= 0 ) |
| { |
| // base isn't used in preceding fields |
| // -> look for other groups using the same base |
| |
| //! get array of groups (or indexes) before loop? |
| ScDPItemData aMemberData; |
| rMember.FillItemData( aMemberData ); |
| long nInitCount = rInitState.GetCount(); |
| const long* pInitSource = rInitState.GetSource(); |
| /*const ScDPItemData* pInitNames = rInitState.GetNames();*/ |
| const SCROW* pInitNames = rInitState.GetNameIds(); |
| for (long nInitPos=0; nInitPos<nInitCount && bInclude; nInitPos++) |
| if ( pResultData->GetGroupBase( pInitSource[nInitPos] ) == nGroupBase ) |
| { |
| // same base (hierarchy between the two groups is irrelevant) |
| bInclude = pResultData->HasCommonElement( pInitNames[nInitPos], pInitSource[nInitPos], |
| aMemberData, nDimSource ); |
| } |
| } |
| |
| return bInclude; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| ScDPResultDimension::ScDPResultDimension( const ScDPResultData* pData ) : |
| pResultData( pData ), |
| bInitialized( sal_False ), |
| bIsDataLayout( sal_False ), |
| bSortByData( sal_False ), |
| bSortAscending( sal_False ), |
| nSortMeasure( 0 ), |
| bAutoShow( sal_False ), |
| bAutoTopItems( sal_False ), |
| nAutoMeasure( 0 ), |
| nAutoCount( 0 ) |
| { |
| } |
| |
| ScDPResultDimension::~ScDPResultDimension() |
| { |
| for( int i = maMemberArray.size () ; i-- > 0 ; ) |
| delete maMemberArray[i]; |
| } |
| |
| ScDPResultMember *ScDPResultDimension::FindMember( SCROW iData ) const |
| { |
| if( bIsDataLayout ) |
| return maMemberArray[0]; |
| |
| MemberHash::const_iterator aRes = maMemberHash.find( iData ); |
| if( aRes != maMemberHash.end()) { |
| if ( aRes->second->IsNamedItem( iData ) ) |
| return aRes->second; |
| DBG_ERROR("problem! hash result is not the same as IsNamedItem"); |
| } |
| |
| unsigned int i; |
| unsigned int nCount = maMemberArray.size(); |
| ScDPResultMember* pResultMember; |
| for( i = 0; i < nCount ; i++ ) |
| { |
| pResultMember = maMemberArray[i]; |
| if ( pResultMember->IsNamedItem( iData ) ) |
| return pResultMember; |
| } |
| return NULL; |
| } |
| |
| void ScDPResultDimension::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, |
| size_t nPos, ScDPInitState& rInitState, sal_Bool bInitChild /*= sal_True */ ) |
| { |
| if (nPos >= ppDim.size() || nPos >= ppLev.size()) |
| { |
| bInitialized = true; |
| return; |
| } |
| |
| ScDPDimension* pThisDim = ppDim[nPos]; |
| ScDPLevel* pThisLevel = ppLev[nPos]; |
| |
| if (!pThisDim || !pThisLevel) |
| { |
| bInitialized = true; |
| return; |
| } |
| |
| bIsDataLayout = pThisDim->getIsDataLayoutDimension(); // member |
| aDimensionName = pThisDim->getName(); // member |
| |
| // Check the autoshow setting. If it's enabled, store the settings. |
| const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow(); |
| if ( rAutoInfo.IsEnabled ) |
| { |
| bAutoShow = sal_True; |
| bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP ); |
| nAutoMeasure = pThisLevel->GetAutoMeasure(); |
| nAutoCount = rAutoInfo.ItemCount; |
| } |
| |
| // Check the sort info, and store the settings if appropriate. |
| const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo(); |
| if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA ) |
| { |
| bSortByData = sal_True; |
| bSortAscending = rSortInfo.IsAscending; |
| nSortMeasure = pThisLevel->GetSortMeasure(); |
| } |
| |
| // global order is used to initialize aMembers, so it doesn't have to be looked at later |
| const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder(); |
| |
| long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim? |
| ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource ); |
| |
| // Now, go through all members and initialize them. |
| ScDPMembers* pMembers = pThisLevel->GetMembersObject(); |
| long nMembCount = pMembers->getCount(); |
| for ( long i=0; i<nMembCount; i++ ) |
| { |
| long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; |
| |
| ScDPMember* pMember = pMembers->getByIndex(nSorted); |
| if ( aCompare.IsIncluded( *pMember ) ) |
| { |
| ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember); |
| ScDPResultMember* pNew = AddMember( aData ); |
| |
| rInitState.AddMember( nDimSource, /*aMemberData*/pNew->GetDataId() ); |
| pNew->InitFrom( ppDim, ppLev, nPos+1, rInitState, bInitChild ); |
| rInitState.RemoveMember(); |
| } |
| } |
| bInitialized = sal_True; |
| } |
| |
| void ScDPResultDimension::LateInitFrom( LateInitParams& rParams/* const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev*/, |
| const vector<SCROW>& pItemData, size_t nPos, |
| ScDPInitState& rInitState ) |
| // End Comments |
| { |
| if ( rParams.IsEnd( nPos ) ) |
| return; |
| #ifdef DBG_UTIL |
| DBG_ASSERT( nPos <= pItemData.size(), ByteString::CreateFromInt32( pItemData.size()).GetBuffer() ); |
| #endif |
| ScDPDimension* pThisDim = rParams.GetDim( nPos ); |
| ScDPLevel* pThisLevel = rParams.GetLevel( nPos ); |
| SCROW rThisData = pItemData[nPos]; |
| |
| if (!pThisDim || !pThisLevel) |
| return; |
| |
| long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim? |
| |
| sal_Bool bShowEmpty = pThisLevel->getShowEmpty(); |
| |
| if ( !bInitialized ) |
| { // init some values |
| // create all members at the first call (preserve order) |
| bIsDataLayout = pThisDim->getIsDataLayoutDimension(); |
| aDimensionName = pThisDim->getName(); |
| |
| const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow(); |
| if ( rAutoInfo.IsEnabled ) |
| { |
| bAutoShow = sal_True; |
| bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP ); |
| nAutoMeasure = pThisLevel->GetAutoMeasure(); |
| nAutoCount = rAutoInfo.ItemCount; |
| } |
| |
| const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo(); |
| if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA ) |
| { |
| bSortByData = sal_True; |
| bSortAscending = rSortInfo.IsAscending; |
| nSortMeasure = pThisLevel->GetSortMeasure(); |
| } |
| } |
| |
| bool bLateInitAllMembers= bIsDataLayout || rParams.GetInitAllChild() || bShowEmpty; |
| |
| if ( !bLateInitAllMembers ) |
| { |
| ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel); |
| bLateInitAllMembers = pMembers->IsHasHideDetailsMembers(); |
| #ifdef DBG_UTIL |
| DBG_TRACESTR( aDimensionName ) |
| if ( pMembers->IsHasHideDetailsMembers() ) |
| DBG_TRACE ( "HasHideDetailsMembers" ); |
| #endif |
| pMembers->SetHasHideDetailsMembers( sal_False ); |
| } |
| |
| bool bNewAllMembers =(!rParams.IsRow()) || nPos == 0 || bLateInitAllMembers ; |
| |
| if (bNewAllMembers ) |
| { |
| // global order is used to initialize aMembers, so it doesn't have to be looked at later |
| if ( !bInitialized ) |
| { //init all members |
| const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder(); |
| |
| ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource ); |
| ScDPMembers* pMembers = pThisLevel->GetMembersObject(); |
| long nMembCount = pMembers->getCount(); |
| for ( long i=0; i<nMembCount; i++ ) |
| { |
| long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; |
| |
| ScDPMember* pMember = pMembers->getByIndex(nSorted); |
| if ( aCompare.IsIncluded( *pMember ) ) |
| { // add all members |
| ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember ); |
| AddMember( aData ); |
| } |
| } |
| bInitialized = sal_True; // don't call again, even if no members were included |
| } |
| // initialize only specific member (or all if "show empty" flag is set) |
| if ( bLateInitAllMembers ) |
| { |
| long nCount = maMemberArray.size(); |
| for (long i=0; i<nCount; i++) |
| { |
| ScDPResultMember* pResultMember = maMemberArray[i]; |
| |
| // check show empty |
| sal_Bool bAllChildren = sal_False; |
| if( bShowEmpty ) |
| { |
| if ( pResultMember->IsNamedItem( rThisData ) ) |
| bAllChildren = sal_False; |
| else |
| bAllChildren = sal_True; |
| } |
| rParams.SetInitAllChildren( bAllChildren ); |
| rInitState.AddMember( nDimSource, pResultMember->GetDataId() ); |
| pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState ); |
| rInitState.RemoveMember(); |
| } |
| } |
| else |
| { |
| ScDPResultMember* pResultMember = FindMember( rThisData ); |
| if( NULL != pResultMember ) |
| { |
| //DBG_TRACE( "ScDPResultDimension::LateInitFrom"); |
| // DBG_TRACESTR( pResultMember->GetDPMember()->GetNameStr()); |
| |
| rInitState.AddMember( nDimSource, pResultMember->GetDataId() ); |
| pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState ); |
| rInitState.RemoveMember(); |
| } |
| } |
| } |
| else |
| InitWithMembers( rParams, pItemData, nPos, rInitState ); |
| } |
| |
| long ScDPResultDimension::GetSize(long nMeasure) const |
| { |
| long nTotal = 0; |
| long nMemberCount = maMemberArray.size(); |
| if (bIsDataLayout) |
| { |
| DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, |
| "DataLayout dimension twice?"); |
| // repeat first member... |
| nTotal = nMemberCount * maMemberArray[0]->GetSize(0); // all measures have equal size |
| } |
| else |
| { |
| // add all members |
| for (long nMem=0; nMem<nMemberCount; nMem++) |
| nTotal += maMemberArray[nMem]->GetSize(nMeasure); |
| } |
| return nTotal; |
| } |
| |
| bool ScDPResultDimension::IsValidEntry( const vector< SCROW >& aMembers ) const |
| { |
| if (aMembers.empty()) |
| return false; |
| |
| const ScDPResultMember* pMember = FindMember( aMembers[0] ); |
| if ( NULL != pMember ) |
| return pMember->IsValidEntry( aMembers ); |
| #ifdef DBG_UTIL |
| ByteString strTemp ("IsValidEntry: Member not found, DimName = " ); |
| strTemp += ByteString( GetName(), RTL_TEXTENCODING_UTF8 ); |
| DBG_TRACE( strTemp.GetBuffer() ); |
| // DBG_ERROR("IsValidEntry: Member not found"); |
| #endif |
| return false; |
| } |
| |
| void ScDPResultDimension::ProcessData( const vector< SCROW >& aMembers, |
| const ScDPResultDimension* pDataDim, |
| const vector< SCROW >& aDataMembers, |
| const vector<ScDPValueData>& aValues ) const |
| { |
| if (aMembers.empty()) |
| return; |
| |
| ScDPResultMember* pMember = FindMember( aMembers[0] ); |
| if ( NULL != pMember ) |
| { |
| vector</*ScDPItemData*/SCROW > aChildMembers; |
| if (aMembers.size() > 1) |
| { |
| vector</*ScDPItemData*/SCROW >::const_iterator itr = aMembers.begin(); |
| aChildMembers.insert(aChildMembers.begin(), ++itr, aMembers.end()); |
| } |
| pMember->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues ); |
| return; |
| } |
| |
| DBG_ERROR("ProcessData: Member not found"); |
| } |
| |
| void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences, |
| long nStart, long nMeasure ) |
| { |
| long nPos = nStart; |
| long nCount = maMemberArray.size(); |
| |
| for (long i=0; i<nCount; i++) |
| { |
| long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i]; |
| |
| ScDPResultMember* pMember = maMemberArray[nSorted]; |
| // in data layout dimension, use first member with different measures/names |
| if ( bIsDataLayout ) |
| { |
| bool bTotalResult = false; |
| String aMbrName = pResultData->GetMeasureDimensionName( nSorted ); |
| String aMbrCapt = pResultData->GetMeasureString( nSorted, sal_False, SUBTOTAL_FUNC_NONE, bTotalResult ); |
| maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, sal_False, &aMbrName, &aMbrCapt ); |
| } |
| else if ( pMember->IsVisible() ) |
| pMember->FillMemberResults( pSequences, nPos, nMeasure, sal_False, NULL, NULL ); |
| // nPos is modified |
| } |
| } |
| |
| void ScDPResultDimension::FillDataResults( const ScDPResultMember* pRefMember, |
| uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, |
| long nRow, long nMeasure ) const |
| { |
| long nMemberRow = nRow; |
| long nMemberMeasure = nMeasure; |
| long nCount = maMemberArray.size(); |
| for (long i=0; i<nCount; i++) |
| { |
| long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i]; |
| |
| const ScDPResultMember* pMember; |
| if (bIsDataLayout) |
| { |
| DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, |
| "DataLayout dimension twice?"); |
| pMember = maMemberArray[0]; |
| nMemberMeasure = nSorted; |
| } |
| else |
| pMember = maMemberArray[nSorted]; |
| |
| if ( pMember->IsVisible() ) |
| pMember->FillDataResults( pRefMember, rSequence, nMemberRow, nMemberMeasure ); |
| // nMemberRow is modified |
| } |
| } |
| |
| void ScDPResultDimension::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const |
| { |
| long nMemberMeasure = nMeasure; |
| long nCount = maMemberArray.size(); |
| for (long i=0; i<nCount; i++) |
| { |
| const ScDPResultMember* pMember; |
| if (bIsDataLayout) |
| { |
| DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, |
| "DataLayout dimension twice?"); |
| pMember = maMemberArray[0]; |
| nMemberMeasure = i; |
| } |
| else |
| pMember = maMemberArray[i]; |
| |
| if ( pMember->IsVisible() ) |
| pMember->UpdateDataResults( pRefMember, nMemberMeasure ); |
| } |
| } |
| |
| void ScDPResultDimension::SortMembers( ScDPResultMember* pRefMember ) |
| { |
| long nCount = maMemberArray.size(); |
| |
| if ( bSortByData ) |
| { |
| // sort members |
| |
| DBG_ASSERT( aMemberOrder.empty(), "sort twice?" ); |
| aMemberOrder.resize( nCount ); |
| for (long nPos=0; nPos<nCount; nPos++) |
| aMemberOrder[nPos] = nPos; |
| |
| ScDPRowMembersOrder aComp( *this, nSortMeasure, bSortAscending ); |
| ::std::sort( aMemberOrder.begin(), aMemberOrder.end(), aComp ); |
| } |
| |
| // handle children |
| |
| // for data layout, call only once - sorting measure is always taken from settings |
| long nLoopCount = bIsDataLayout ? 1 : nCount; |
| for (long i=0; i<nLoopCount; i++) |
| { |
| ScDPResultMember* pMember = maMemberArray[i]; |
| if ( pMember->IsVisible() ) |
| pMember->SortMembers( pRefMember ); |
| } |
| } |
| |
| void ScDPResultDimension::DoAutoShow( ScDPResultMember* pRefMember ) |
| { |
| long nCount = maMemberArray.size(); |
| |
| // handle children first, before changing the visible state |
| |
| // for data layout, call only once - sorting measure is always taken from settings |
| long nLoopCount = bIsDataLayout ? 1 : nCount; |
| for (long i=0; i<nLoopCount; i++) |
| { |
| ScDPResultMember* pMember = maMemberArray[i]; |
| if ( pMember->IsVisible() ) |
| pMember->DoAutoShow( pRefMember ); |
| } |
| |
| if ( bAutoShow && nAutoCount > 0 && nAutoCount < nCount ) |
| { |
| // establish temporary order, hide remaining members |
| |
| ScMemberSortOrder aAutoOrder; |
| aAutoOrder.resize( nCount ); |
| long nPos; |
| for (nPos=0; nPos<nCount; nPos++) |
| aAutoOrder[nPos] = nPos; |
| |
| ScDPRowMembersOrder aComp( *this, nAutoMeasure, !bAutoTopItems ); |
| ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp ); |
| |
| // look for equal values to the last included one |
| |
| long nIncluded = nAutoCount; |
| const ScDPResultMember* pMember1 = maMemberArray[aAutoOrder[nIncluded - 1]]; |
| const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : NULL; |
| sal_Bool bContinue = sal_True; |
| while ( bContinue ) |
| { |
| bContinue = sal_False; |
| if ( nIncluded < nCount ) |
| { |
| const ScDPResultMember* pMember2 = maMemberArray[aAutoOrder[nIncluded]]; |
| const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : NULL; |
| |
| if ( lcl_IsEqual( pDataMember1, pDataMember2, nAutoMeasure ) ) |
| { |
| ++nIncluded; // include more members if values are equal |
| bContinue = sal_True; |
| } |
| } |
| } |
| |
| // hide the remaining members |
| |
| for (nPos = nIncluded; nPos < nCount; nPos++) |
| { |
| ScDPResultMember* pMember = maMemberArray[aAutoOrder[nPos]]; |
| pMember->SetAutoHidden(); |
| } |
| } |
| } |
| |
| void ScDPResultDimension::ResetResults() |
| { |
| long nCount = maMemberArray.size(); |
| for (long i=0; i<nCount; i++) |
| { |
| // sort order doesn't matter |
| ScDPResultMember* pMember = maMemberArray[bIsDataLayout ? 0 : i]; |
| pMember->ResetResults( sal_False ); |
| } |
| } |
| |
| long ScDPResultDimension::GetSortedIndex( long nUnsorted ) const |
| { |
| return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted]; |
| } |
| |
| void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure, |
| ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const |
| { |
| const ScDPResultMember* pMember; |
| long nMemberMeasure = nMeasure; |
| long nCount = maMemberArray.size(); |
| for (long i=0; i<nCount; i++) |
| { |
| long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i]; |
| |
| if (bIsDataLayout) |
| { |
| DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, |
| "DataLayout dimension twice?"); |
| pMember = maMemberArray[0]; |
| nMemberMeasure = nSorted; |
| } |
| else |
| pMember = maMemberArray[nSorted]; |
| |
| if ( pMember->IsVisible() ) |
| { |
| if ( bIsDataLayout ) |
| rRunning.AddRowIndex( 0, 0 ); |
| else |
| rRunning.AddRowIndex( i, nSorted ); |
| pMember->UpdateRunningTotals( pRefMember, nMemberMeasure, rRunning, rTotals ); |
| rRunning.RemoveRowIndex(); |
| } |
| } |
| } |
| |
| ScDPDataMember* ScDPResultDimension::GetRowReferenceMember( const ScDPRelativePos* pRelativePos, const String* pName, |
| const long* pRowIndexes, const long* pColIndexes ) const |
| { |
| // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL) |
| |
| DBG_ASSERT( pRelativePos == NULL || pName == NULL, "can't use position and name" ); |
| |
| ScDPDataMember* pColMember = NULL; |
| |
| sal_Bool bFirstExisting = ( pRelativePos == NULL && pName == NULL ); |
| long nMemberCount = maMemberArray.size(); |
| long nMemberIndex = 0; // unsorted |
| long nDirection = 1; // forward if no relative position is used |
| if ( pRelativePos ) |
| { |
| nDirection = pRelativePos->nDirection; |
| nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below |
| |
| DBG_ASSERT( nDirection == 1 || nDirection == -1, "Direction must be 1 or -1" ); |
| } |
| else if ( pName ) |
| { |
| // search for named member |
| |
| const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)]; |
| |
| //! use ScDPItemData, as in ScDPDimension::IsValidPage? |
| while ( pRowMember && pRowMember->GetName() != *pName ) |
| { |
| ++nMemberIndex; |
| if ( nMemberIndex < nMemberCount ) |
| pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)]; |
| else |
| pRowMember = NULL; |
| } |
| } |
| |
| sal_Bool bContinue = sal_True; |
| while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nMemberCount ) |
| { |
| const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)]; |
| |
| // get child members by given indexes |
| |
| const long* pNextRowIndex = pRowIndexes; |
| while ( *pNextRowIndex >= 0 && pRowMember ) |
| { |
| const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension(); |
| if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() ) |
| pRowMember = pRowChild->GetMember( *pNextRowIndex ); |
| else |
| pRowMember = NULL; |
| ++pNextRowIndex; |
| } |
| |
| if ( pRowMember && pRelativePos ) |
| { |
| // Skip the member if it has hidden details |
| // (because when looking for the details, it is skipped, too). |
| // Also skip if the member is invisible because it has no data, |
| // for consistent ordering. |
| if ( pRowMember->HasHiddenDetails() || !pRowMember->IsVisible() ) |
| pRowMember = NULL; |
| } |
| |
| if ( pRowMember ) |
| { |
| pColMember = pRowMember->GetDataRoot(); |
| |
| const long* pNextColIndex = pColIndexes; |
| while ( *pNextColIndex >= 0 && pColMember ) |
| { |
| const ScDPDataDimension* pColChild = pColMember->GetChildDimension(); |
| if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() ) |
| pColMember = pColChild->GetMember( *pNextColIndex ); |
| else |
| pColMember = NULL; |
| ++pNextColIndex; |
| } |
| } |
| |
| // continue searching only if looking for first existing or relative position |
| bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) ); |
| nMemberIndex += nDirection; |
| } |
| |
| return pColMember; |
| } |
| |
| // static |
| ScDPDataMember* ScDPResultDimension::GetColReferenceMember( const ScDPRelativePos* pRelativePos, const String* pName, |
| long nRefDimPos, const ScDPRunningTotalState& rRunning ) |
| { |
| DBG_ASSERT( pRelativePos == NULL || pName == NULL, "can't use position and name" ); |
| |
| const long* pColIndexes = rRunning.GetColIndexes(); |
| const long* pRowIndexes = rRunning.GetRowIndexes(); |
| |
| // get own row member using all indexes |
| |
| const ScDPResultMember* pRowMember = rRunning.GetRowResRoot(); |
| ScDPDataMember* pColMember = NULL; |
| |
| const long* pNextRowIndex = pRowIndexes; |
| while ( *pNextRowIndex >= 0 && pRowMember ) |
| { |
| const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension(); |
| if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() ) |
| pRowMember = pRowChild->GetMember( *pNextRowIndex ); |
| else |
| pRowMember = NULL; |
| ++pNextRowIndex; |
| } |
| |
| // get column (data) members before the reference field |
| //! pass rRowParent from ScDPDataMember::UpdateRunningTotals instead |
| |
| if ( pRowMember ) |
| { |
| pColMember = pRowMember->GetDataRoot(); |
| |
| const long* pNextColIndex = pColIndexes; |
| long nColSkipped = 0; |
| while ( *pNextColIndex >= 0 && pColMember && nColSkipped < nRefDimPos ) |
| { |
| const ScDPDataDimension* pColChild = pColMember->GetChildDimension(); |
| if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() ) |
| pColMember = pColChild->GetMember( *pNextColIndex ); |
| else |
| pColMember = NULL; |
| ++pNextColIndex; |
| ++nColSkipped; |
| } |
| } |
| |
| // get column member for the reference field |
| |
| if ( pColMember ) |
| { |
| const ScDPDataDimension* pReferenceDim = pColMember->GetChildDimension(); |
| if ( pReferenceDim ) |
| { |
| long nReferenceCount = pReferenceDim->GetMemberCount(); |
| |
| sal_Bool bFirstExisting = ( pRelativePos == NULL && pName == NULL ); |
| long nMemberIndex = 0; // unsorted |
| long nDirection = 1; // forward if no relative position is used |
| pColMember = NULL; // don't use parent dimension's member if none found |
| if ( pRelativePos ) |
| { |
| nDirection = pRelativePos->nDirection; |
| nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below |
| } |
| else if ( pName ) |
| { |
| // search for named member |
| |
| pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) ); |
| |
| //! use ScDPItemData, as in ScDPDimension::IsValidPage? |
| while ( pColMember && pColMember->GetName() != *pName ) |
| { |
| ++nMemberIndex; |
| if ( nMemberIndex < nReferenceCount ) |
| pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) ); |
| else |
| pColMember = NULL; |
| } |
| } |
| |
| sal_Bool bContinue = sal_True; |
| while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nReferenceCount ) |
| { |
| pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) ); |
| |
| // get column members below the reference field |
| |
| const long* pNextColIndex = pColIndexes + nRefDimPos + 1; |
| while ( *pNextColIndex >= 0 && pColMember ) |
| { |
| const ScDPDataDimension* pColChild = pColMember->GetChildDimension(); |
| if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() ) |
| pColMember = pColChild->GetMember( *pNextColIndex ); |
| else |
| pColMember = NULL; |
| ++pNextColIndex; |
| } |
| |
| if ( pColMember && pRelativePos ) |
| { |
| // Skip the member if it has hidden details |
| // (because when looking for the details, it is skipped, too). |
| // Also skip if the member is invisible because it has no data, |
| // for consistent ordering. |
| if ( pColMember->HasHiddenDetails() || !pColMember->IsVisible() ) |
| pColMember = NULL; |
| } |
| |
| // continue searching only if looking for first existing or relative position |
| bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) ); |
| nMemberIndex += nDirection; |
| } |
| } |
| else |
| pColMember = NULL; |
| } |
| |
| return pColMember; |
| } |
| |
| void ScDPResultDimension::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const |
| { |
| String aDimName = bIsDataLayout ? String::CreateFromAscii("(data layout)") : GetName(); |
| lcl_DumpRow( String::CreateFromAscii("ScDPResultDimension"), aDimName, NULL, pDoc, rPos ); |
| |
| SCROW nStartRow = rPos.Row(); |
| |
| long nCount = bIsDataLayout ? 1 : maMemberArray.size(); |
| for (long i=0; i<nCount; i++) |
| { |
| const ScDPResultMember* pMember = maMemberArray[i]; |
| pMember->DumpState( pRefMember, pDoc, rPos ); |
| } |
| |
| lcl_Indent( pDoc, nStartRow, rPos ); |
| } |
| |
| long ScDPResultDimension::GetMemberCount() const |
| { |
| return maMemberArray.size(); |
| } |
| |
| const ScDPResultMember* ScDPResultDimension::GetMember(long n) const |
| { |
| return maMemberArray[n]; |
| } |
| ScDPResultMember* ScDPResultDimension::GetMember(long n) |
| { |
| return maMemberArray[n]; |
| } |
| |
| ScDPResultDimension* ScDPResultDimension::GetFirstChildDimension() const |
| { |
| if ( maMemberArray.size() > 0 ) |
| return maMemberArray[0]->GetChildDimension(); |
| else |
| return NULL; |
| } |
| |
| void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData& rData) const |
| { |
| if (IsDataLayout()) |
| return; |
| |
| MemberArray::const_iterator itr = maMemberArray.begin(), itrEnd = maMemberArray.end(); |
| |
| for (;itr != itrEnd; ++itr) |
| { |
| ScDPResultMember* pMember = *itr; |
| if (pMember->IsValid()) |
| { |
| ScDPItemData aItem; |
| pMember->FillItemData(aItem); |
| rData.addVisibleMember(GetName(), aItem); |
| pMember->FillVisibilityData(rData); |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| ScDPDataDimension::ScDPDataDimension( const ScDPResultData* pData ) : |
| pResultData( pData ), |
| pResultDimension( NULL ), |
| bIsDataLayout( sal_False ) |
| { |
| } |
| |
| ScDPDataDimension::~ScDPDataDimension() |
| { |
| } |
| |
| void ScDPDataDimension::InitFrom( const ScDPResultDimension* pDim ) |
| { |
| if (!pDim) |
| return; |
| |
| pResultDimension = pDim; |
| bIsDataLayout = pDim->IsDataLayout(); |
| |
| // Go through all result members under the given result dimension, and |
| // create a new data member instance for each result member. |
| long nCount = pDim->GetMemberCount(); |
| for (long i=0; i<nCount; i++) |
| { |
| const ScDPResultMember* pResMem = pDim->GetMember(i); |
| |
| ScDPDataMember* pNew = new ScDPDataMember( pResultData, pResMem ); |
| aMembers.Insert( pNew, aMembers.Count() ); |
| |
| if ( !pResultData->IsLateInit() ) |
| { |
| // with LateInit, pResMem hasn't necessarily been initialized yet, |
| // so InitFrom for the new result member is called from its ProcessData method |
| |
| const ScDPResultDimension* pChildDim = pResMem->GetChildDimension(); |
| if ( pChildDim ) |
| pNew->InitFrom( pChildDim ); |
| } |
| } |
| } |
| |
| void ScDPDataDimension::ProcessData( const vector< SCROW >& aDataMembers, const vector<ScDPValueData>& aValues, |
| const ScDPSubTotalState& rSubState ) |
| { |
| // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked |
| |
| long nCount = aMembers.Count(); |
| for (long i=0; i<nCount; i++) |
| { |
| ScDPDataMember* pMember = aMembers[(sal_uInt16)i]; |
| |
| // always first member for data layout dim |
| if ( bIsDataLayout || ( !aDataMembers.empty() && pMember->IsNamedItem(aDataMembers[0]) ) ) |
| { |
| vector</*ScDPItemData*/SCROW> aChildDataMembers; |
| if (aDataMembers.size() > 1) |
| { |
| vector</*ScDPItemData*/SCROW >::const_iterator itr = aDataMembers.begin(); |
| aChildDataMembers.insert(aChildDataMembers.begin(), ++itr, aDataMembers.end()); |
| } |
| pMember->ProcessData( aChildDataMembers, aValues, rSubState ); |
| return; |
| } |
| } |
| |
| DBG_ERROR("ProcessData: Member not found"); |
| } |
| |
| void ScDPDataDimension::FillDataRow( const ScDPResultDimension* pRefDim, |
| uno::Sequence<sheet::DataResult>& rSequence, |
| long nCol, long nMeasure, sal_Bool bIsSubTotalRow, |
| const ScDPSubTotalState& rSubState ) const |
| { |
| DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); |
| DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); |
| |
| const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder(); |
| |
| long nMemberMeasure = nMeasure; |
| long nMemberCol = nCol; |
| long nCount = aMembers.Count(); |
| for (long i=0; i<nCount; i++) |
| { |
| long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i]; |
| |
| long nMemberPos = nSorted; |
| if (bIsDataLayout) |
| { |
| DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, |
| "DataLayout dimension twice?"); |
| nMemberPos = 0; |
| nMemberMeasure = nSorted; |
| } |
| |
| const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos); |
| if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember::FillDataRow ??? |
| { |
| const ScDPDataMember* pDataMember = aMembers[(sal_uInt16)nMemberPos]; |
| pDataMember->FillDataRow( pRefMember, rSequence, nMemberCol, nMemberMeasure, bIsSubTotalRow, rSubState ); |
| // nMemberCol is modified |
| } |
| } |
| } |
| |
| void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension* pRefDim, |
| long nMeasure, sal_Bool bIsSubTotalRow, |
| const ScDPSubTotalState& rSubState ) const |
| { |
| DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); |
| DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); |
| |
| long nMemberMeasure = nMeasure; |
| long nCount = aMembers.Count(); |
| for (long i=0; i<nCount; i++) |
| { |
| long nMemberPos = i; |
| if (bIsDataLayout) |
| { |
| DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, |
| "DataLayout dimension twice?"); |
| nMemberPos = 0; |
| nMemberMeasure = i; |
| } |
| |
| // Calculate must be called even if the member is not visible (for use as reference value) |
| const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos); |
| ScDPDataMember* pDataMember = aMembers[(sal_uInt16)nMemberPos]; |
| pDataMember->UpdateDataRow( pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState ); |
| } |
| } |
| |
| void ScDPDataDimension::SortMembers( ScDPResultDimension* pRefDim ) |
| { |
| long nCount = aMembers.Count(); |
| |
| if ( pRefDim->IsSortByData() ) |
| { |
| // sort members |
| |
| ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder(); |
| DBG_ASSERT( rMemberOrder.empty(), "sort twice?" ); |
| rMemberOrder.resize( nCount ); |
| for (long nPos=0; nPos<nCount; nPos++) |
| rMemberOrder[nPos] = nPos; |
| |
| ScDPColMembersOrder aComp( *this, pRefDim->GetSortMeasure(), pRefDim->IsSortAscending() ); |
| ::std::sort( rMemberOrder.begin(), rMemberOrder.end(), aComp ); |
| } |
| |
| // handle children |
| |
| DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); |
| DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); |
| |
| // for data layout, call only once - sorting measure is always taken from settings |
| long nLoopCount = bIsDataLayout ? 1 : nCount; |
| for (long i=0; i<nLoopCount; i++) |
| { |
| ScDPResultMember* pRefMember = pRefDim->GetMember(i); |
| if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ??? |
| { |
| ScDPDataMember* pDataMember = aMembers[(sal_uInt16)i]; |
| pDataMember->SortMembers( pRefMember ); |
| } |
| } |
| } |
| |
| void ScDPDataDimension::DoAutoShow( ScDPResultDimension* pRefDim ) |
| { |
| long nCount = aMembers.Count(); |
| |
| // handle children first, before changing the visible state |
| |
| DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); |
| DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); |
| |
| // for data layout, call only once - sorting measure is always taken from settings |
| long nLoopCount = bIsDataLayout ? 1 : nCount; |
| for (long i=0; i<nLoopCount; i++) |
| { |
| ScDPResultMember* pRefMember = pRefDim->GetMember(i); |
| if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ??? |
| { |
| ScDPDataMember* pDataMember = aMembers[(sal_uInt16)i]; |
| pDataMember->DoAutoShow( pRefMember ); |
| } |
| } |
| |
| if ( pRefDim->IsAutoShow() && pRefDim->GetAutoCount() > 0 && pRefDim->GetAutoCount() < nCount ) |
| { |
| // establish temporary order, hide remaining members |
| |
| ScMemberSortOrder aAutoOrder; |
| aAutoOrder.resize( nCount ); |
| long nPos; |
| for (nPos=0; nPos<nCount; nPos++) |
| aAutoOrder[nPos] = nPos; |
| |
| ScDPColMembersOrder aComp( *this, pRefDim->GetAutoMeasure(), !pRefDim->IsAutoTopItems() ); |
| ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp ); |
| |
| // look for equal values to the last included one |
| |
| long nIncluded = pRefDim->GetAutoCount(); |
| ScDPDataMember* pDataMember1 = aMembers[(sal_uInt16)aAutoOrder[nIncluded - 1]]; |
| if ( !pDataMember1->IsVisible() ) |
| pDataMember1 = NULL; |
| sal_Bool bContinue = sal_True; |
| while ( bContinue ) |
| { |
| bContinue = sal_False; |
| if ( nIncluded < nCount ) |
| { |
| ScDPDataMember* pDataMember2 = aMembers[(sal_uInt16)aAutoOrder[nIncluded]]; |
| if ( !pDataMember2->IsVisible() ) |
| pDataMember2 = NULL; |
| |
| if ( lcl_IsEqual( pDataMember1, pDataMember2, pRefDim->GetAutoMeasure() ) ) |
| { |
| ++nIncluded; // include more members if values are equal |
| bContinue = sal_True; |
| } |
| } |
| } |
| |
| // hide the remaining members |
| |
| for (nPos = nIncluded; nPos < nCount; nPos++) |
| { |
| ScDPResultMember* pMember = pRefDim->GetMember(aAutoOrder[nPos]); |
| pMember->SetAutoHidden(); |
| } |
| } |
| } |
| |
| void ScDPDataDimension::ResetResults() |
| { |
| long nCount = aMembers.Count(); |
| for (long i=0; i<nCount; i++) |
| { |
| // sort order doesn't matter |
| |
| long nMemberPos = bIsDataLayout ? 0 : i; |
| ScDPDataMember* pDataMember = aMembers[(sal_uInt16)nMemberPos]; |
| pDataMember->ResetResults(); |
| } |
| } |
| |
| long ScDPDataDimension::GetSortedIndex( long nUnsorted ) const |
| { |
| if (!pResultDimension) |
| return nUnsorted; |
| |
| const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder(); |
| return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted]; |
| } |
| |
| void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension* pRefDim, |
| long nMeasure, sal_Bool bIsSubTotalRow, |
| const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning, |
| ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) const |
| { |
| DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); |
| DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); |
| |
| long nMemberMeasure = nMeasure; |
| long nCount = aMembers.Count(); |
| for (long i=0; i<nCount; i++) |
| { |
| const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder(); |
| long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i]; |
| |
| long nMemberPos = nSorted; |
| if (bIsDataLayout) |
| { |
| DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, |
| "DataLayout dimension twice?"); |
| nMemberPos = 0; |
| nMemberMeasure = nSorted; |
| } |
| |
| const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos); |
| if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember::UpdateRunningTotals ??? |
| { |
| if ( bIsDataLayout ) |
| rRunning.AddColIndex( 0, 0 ); |
| else |
| rRunning.AddColIndex( i, nSorted ); |
| |
| ScDPDataMember* pDataMember = aMembers[(sal_uInt16)nMemberPos]; |
| pDataMember->UpdateRunningTotals( pRefMember, nMemberMeasure, |
| bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent ); |
| |
| rRunning.RemoveColIndex(); |
| } |
| } |
| } |
| |
| void ScDPDataDimension::DumpState( const ScDPResultDimension* pRefDim, ScDocument* pDoc, ScAddress& rPos ) const |
| { |
| String aDimName = String::CreateFromAscii( bIsDataLayout ? "(data layout)" : "(unknown)" ); |
| lcl_DumpRow( String::CreateFromAscii("ScDPDataDimension"), aDimName, NULL, pDoc, rPos ); |
| |
| SCROW nStartRow = rPos.Row(); |
| |
| long nCount = bIsDataLayout ? 1 : aMembers.Count(); |
| for (long i=0; i<nCount; i++) |
| { |
| const ScDPResultMember* pRefMember = pRefDim->GetMember(i); |
| const ScDPDataMember* pDataMember = aMembers[(sal_uInt16)i]; |
| pDataMember->DumpState( pRefMember, pDoc, rPos ); |
| } |
| |
| lcl_Indent( pDoc, nStartRow, rPos ); |
| } |
| |
| long ScDPDataDimension::GetMemberCount() const |
| { |
| return aMembers.Count(); |
| } |
| |
| ScDPDataMember* ScDPDataDimension::GetMember(long n) const |
| { |
| return aMembers[(sal_uInt16)n]; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| ScDPResultVisibilityData::ScDPResultVisibilityData( |
| ScDPSource* pSource) : |
| mpSource(pSource) |
| { |
| } |
| |
| ScDPResultVisibilityData::~ScDPResultVisibilityData() |
| { |
| } |
| |
| void ScDPResultVisibilityData::addVisibleMember(const String& rDimName, const ScDPItemData& rMemberItem) |
| { |
| DimMemberType::iterator itr = maDimensions.find(rDimName); |
| if (itr == maDimensions.end()) |
| { |
| pair<DimMemberType::iterator, bool> r = maDimensions.insert( |
| DimMemberType::value_type(rDimName, VisibleMemberType())); |
| |
| if (!r.second) |
| // insertion failed. |
| return; |
| |
| itr = r.first; |
| } |
| VisibleMemberType& rMem = itr->second; |
| VisibleMemberType::iterator itrMem = rMem.find(rMemberItem); |
| if (itrMem == rMem.end()) |
| rMem.insert(rMemberItem); |
| } |
| |
| void ScDPResultVisibilityData::fillFieldFilters(vector<ScDPCacheTable::Criterion>& rFilters) const |
| { |
| typedef hash_map<String, long, ScStringHashCode> FieldNameMapType; |
| FieldNameMapType aFieldNames; |
| ScDPTableData* pData = mpSource->GetData(); |
| long nColumnCount = pData->GetColumnCount(); |
| for (long i = 0; i < nColumnCount; ++i) |
| { |
| aFieldNames.insert( |
| FieldNameMapType::value_type(pData->getDimensionName(i), i)); |
| } |
| |
| const ScDPDimensions* pDims = mpSource->GetDimensionsObject(); |
| for (DimMemberType::const_iterator itr = maDimensions.begin(), itrEnd = maDimensions.end(); |
| itr != itrEnd; ++itr) |
| { |
| const String& rDimName = itr->first; |
| ScDPCacheTable::Criterion aCri; |
| FieldNameMapType::const_iterator itrField = aFieldNames.find(rDimName); |
| if (itrField == aFieldNames.end()) |
| // This should never happen! |
| continue; |
| |
| long nDimIndex = itrField->second; |
| aCri.mnFieldIndex = static_cast<sal_Int32>(nDimIndex); |
| aCri.mpFilter.reset(new ScDPCacheTable::GroupFilter(/*mrSharedString*/)); |
| |
| ScDPCacheTable::GroupFilter* pGrpFilter = |
| static_cast<ScDPCacheTable::GroupFilter*>(aCri.mpFilter.get()); |
| |
| const VisibleMemberType& rMem = itr->second; |
| for (VisibleMemberType::const_iterator itrMem = rMem.begin(), itrMemEnd = rMem.end(); |
| itrMem != itrMemEnd; ++itrMem) |
| { |
| const ScDPItemData& rMemItem = *itrMem; |
| pGrpFilter->addMatchItem(rMemItem.GetString(), rMemItem.GetValue(), rMemItem.IsValue()); |
| } |
| |
| ScDPDimension* pDim = pDims->getByIndex(nDimIndex); |
| ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)-> |
| GetLevelsObject()->getByIndex(0)->GetMembersObject(); |
| if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(pMembers->getCount())) |
| rFilters.push_back(aCri); |
| } |
| } |
| |
| size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData& r) const |
| { |
| if (r.IsValue()) |
| return static_cast<size_t>(::rtl::math::approxFloor(r.GetValue())); |
| else |
| return rtl_ustr_hashCode_WithLength(r.GetString().GetBuffer(), r.GetString().Len()); |
| } |
| // Wang Xu Ming -- 2009-6-10 |
| // DataPilot Migration |
| SCROW ScDPResultMember::GetDataId( ) const |
| { |
| const ScDPMember* pMemberDesc = GetDPMember(); |
| if (pMemberDesc) |
| return pMemberDesc->GetItemDataId(); |
| return -1; |
| } |
| |
| ScDPResultMember* ScDPResultDimension::AddMember(const ScDPParentDimData &aData ) |
| { |
| ScDPResultMember* pMember = new ScDPResultMember( pResultData, aData, sal_False ); |
| SCROW nDataIndex = pMember->GetDataId(); |
| maMemberArray.push_back( pMember ); |
| |
| if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) ) |
| maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pMember ) ); |
| return pMember; |
| } |
| |
| ResultMembers* ScDPResultDimension::GetResultMember( ScDPDimension* pThisDim, ScDPLevel* pThisLevel ) |
| { |
| ResultMembers* pResultMembers = new ResultMembers(); |
| // global order is used to initialize aMembers, so it doesn't have to be looked at later |
| const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder(); |
| |
| ScDPMembers* pMembers = pThisLevel->GetMembersObject(); |
| long nMembCount = pMembers->getCount(); |
| for ( long i=0; i<nMembCount; i++ ) |
| { |
| long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; |
| ScDPMember* pMember = pMembers->getByIndex(nSorted); |
| if ( NULL == pResultMembers->FindMember( pMember->GetItemDataId() ) ) |
| { |
| ScDPParentDimData* pNew = new ScDPParentDimData( i, pThisDim, pThisLevel, pMember ); |
| pResultMembers->InsertMember( pNew ); |
| } |
| } |
| return pResultMembers; |
| } |
| |
| ScDPResultMember* ScDPResultDimension::InsertMember(ScDPParentDimData *pMemberData) |
| { |
| SCROW nInsert = 0; |
| if ( !lcl_SearchMember( maMemberArray, pMemberData->mnOrder , nInsert ) ) |
| { //Member not exist |
| ScDPResultMember* pNew = new ScDPResultMember( pResultData, *pMemberData, sal_False ); |
| maMemberArray.insert( maMemberArray.begin()+nInsert, pNew ); |
| |
| SCROW nDataIndex = pMemberData->mpMemberDesc->GetItemDataId(); |
| if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) ) |
| maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pNew ) ); |
| return pNew; |
| } |
| return maMemberArray[ nInsert ]; |
| } |
| |
| void ScDPResultDimension:: InitWithMembers( LateInitParams& rParams, |
| const ::std::vector< SCROW >& pItemData, |
| size_t nPos, |
| ScDPInitState& rInitState ) |
| { |
| if ( rParams.IsEnd( nPos ) ) |
| return; |
| ScDPDimension* pThisDim = rParams.GetDim( nPos ); |
| ScDPLevel* pThisLevel = rParams.GetLevel( nPos ); |
| SCROW nDataID = pItemData[nPos]; |
| |
| if (pThisDim && pThisLevel) |
| { |
| long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim? |
| |
| // create all members at the first call (preserve order) |
| ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel); |
| ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource ); |
| // initialize only specific member (or all if "show empty" flag is set) |
| ScDPResultMember* pResultMember = NULL; |
| if ( bInitialized ) |
| pResultMember = FindMember( nDataID ); |
| else |
| bInitialized = sal_True; |
| |
| if ( pResultMember == NULL ) |
| { //only insert found item |
| ScDPParentDimData* pMemberData = pMembers->FindMember( nDataID ); |
| if ( pMemberData && aCompare.IsIncluded( *( pMemberData->mpMemberDesc ) ) ) |
| pResultMember = InsertMember( pMemberData ); |
| } |
| if ( pResultMember ) |
| { |
| // DBG_TRACE( "ScDPResultDimension::InitWithMembers"); |
| // DBG_TRACESTR( pResultMember->GetDPMember()->GetNameStr()); |
| rInitState.AddMember( nDimSource, pResultMember->GetDataId() ); |
| pResultMember->LateInitFrom( rParams /*ppDim, ppLev*/, pItemData, nPos+1 , rInitState ); |
| rInitState.RemoveMember(); |
| } |
| } |
| } |
| |
| ScDPParentDimData* ResultMembers::FindMember( const SCROW& nIndex ) const |
| { |
| DimMemberHash::const_iterator aRes = maMemberHash.find( nIndex ); |
| if( aRes != maMemberHash.end()) { |
| if ( aRes->second->mpMemberDesc && aRes->second->mpMemberDesc->GetItemDataId()==nIndex ) |
| return aRes->second; |
| } |
| return NULL; |
| } |
| void ResultMembers::InsertMember( ScDPParentDimData* pNew ) |
| { |
| if ( !pNew->mpMemberDesc->getShowDetails() ) |
| mbHasHideDetailsMember = sal_True; |
| maMemberHash.insert( std::pair< const SCROW, ScDPParentDimData *>( pNew->mpMemberDesc->GetItemDataId(), pNew ) ); |
| } |
| |
| ResultMembers::ResultMembers(): |
| mbHasHideDetailsMember( sal_False ) |
| { |
| } |
| ResultMembers::~ResultMembers() |
| { |
| for ( DimMemberHash::const_iterator iter = maMemberHash.begin(); iter != maMemberHash.end(); iter++ ) |
| delete iter->second; |
| } |
| // ----------------------------------------------------------------------- |
| LateInitParams::LateInitParams( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, sal_Bool bRow, sal_Bool bInitChild, sal_Bool bAllChildren ): |
| mppDim( ppDim ), |
| mppLev( ppLev ), |
| mbRow( bRow ), |
| mbInitChild( bInitChild ), |
| mbAllChildren( bAllChildren ) |
| { |
| } |
| |
| LateInitParams::~LateInitParams() |
| { |
| } |
| |
| sal_Bool LateInitParams::IsEnd( size_t nPos ) const |
| { |
| return nPos >= mppDim.size(); |
| } |
| |
| // End Comments |
| // Wang Xu Ming -- 2009-8-4 |
| // DataPilot Migration - old defects merge |
| void ScDPResultDimension::CheckShowEmpty( sal_Bool bShow ) |
| { |
| long nCount = maMemberArray.size(); |
| |
| ScDPResultMember* pMember = NULL; |
| for (long i=0; i<nCount; i++) |
| { |
| pMember = maMemberArray.at(i); |
| pMember->CheckShowEmpty( bShow ); |
| } |
| |
| } |
| |
| void ScDPResultMember::CheckShowEmpty( sal_Bool bShow ) |
| { |
| if ( bHasElements ) |
| { |
| ScDPResultDimension* pChildDim = GetChildDimension(); |
| if (pChildDim ) |
| pChildDim->CheckShowEmpty(); |
| } |
| else if ( IsValid() && bInitialized ) |
| { |
| bShow = bShow || ( GetParentLevel() && GetParentLevel()->getShowEmpty() ); |
| if ( bShow ) |
| { |
| SetHasElements(); |
| ScDPResultDimension* pChildDim = GetChildDimension(); |
| if (pChildDim ) |
| pChildDim->CheckShowEmpty( sal_True ); |
| } |
| } |
| }// End Comments |