/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 .
*/
usingnamespace com::sun::star; using ::std::vector; using ::com::sun::star::uno::Sequence; using ::com::sun::star::uno::Any; using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
// Typos are on purpose here, quote from Eike Rathke (see https://gerrit.libreoffice.org/c/core/+/101116): // "The typo is exactly why the SC_SIMPLE_SERVICE_INFO_COMPAT() lists both service names, // the old with the typo and the new corrected one. Correcting the typo in the old name // will make all extensions fail that use it. This is not to be changed."
SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchies, u"ScDPHierarchies"_ustr,
u"com.sun.star.sheet.DataPilotSourceHierarchies"_ustr, u"com.sun.star.sheet.DataPilotSourceHierarcies"_ustr )
SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchy, u"ScDPHierarchy"_ustr,
u"com.sun.star.sheet.DataPilotSourceHierarchy"_ustr, u"com.sun.star.sheet.DataPilotSourceHierarcy"_ustr )
itBeg = maRowDims.begin();
itEnd = maRowDims.end();
it = std::find(itBeg, itEnd, nColumn); if (it != itEnd) return std::distance(itBeg, it);
itBeg = maDataDims.begin();
itEnd = maDataDims.end();
it = std::find(itBeg, itEnd, nColumn); if (it != itEnd) return std::distance(itBeg, it);
itBeg = maPageDims.begin();
itEnd = maPageDims.end();
it = std::find(itBeg, itEnd, nColumn); if (it != itEnd) return std::distance(itBeg, it);
return 0;
}
namespace {
bool testSubTotal( bool& rAllowed, sal_Int32 nColumn, const std::vector<sal_Int32>& rDims, ScDPSource* pSource )
{
rAllowed = true;
std::vector<sal_Int32>::const_iterator it = rDims.begin(), itEnd = rDims.end(); for (; it != itEnd; ++it)
{ if (*it != nColumn) continue;
if ( pSource->IsDataLayoutDimension(nColumn) )
{ // no subtotals for data layout dim, no matter where
rAllowed = false; returntrue;
}
// no subtotals if no other dim but data layout follows
++it; if (it != itEnd && pSource->IsDataLayoutDimension(*it))
++it; if (it == itEnd)
rAllowed = false;
returntrue; // found
}
returnfalse;
}
void removeDim( sal_Int32 nRemove, std::vector<sal_Int32>& rDims )
{
std::vector<sal_Int32>::iterator it = std::find(rDims.begin(), rDims.end(), nRemove); if (it != rDims.end())
rDims.erase(it);
}
}
bool ScDPSource::SubTotalAllowed(sal_Int32 nColumn)
{ //TODO: cache this at ScDPResultData bool bAllowed = true; if ( testSubTotal(bAllowed, nColumn, maColDims, this) ) return bAllowed; if ( testSubTotal(bAllowed, nColumn, maRowDims, this) ) return bAllowed; return bAllowed;
}
void ScDPSource::SetOrientation(sal_Int32 nColumn, sheet::DataPilotFieldOrientation nNew)
{ //TODO: change to no-op if new orientation is equal to old?
// remove from old list
removeDim(nColumn, maColDims);
removeDim(nColumn, maRowDims);
removeDim(nColumn, maDataDims);
removeDim(nColumn, maPageDims);
// add to new list switch (nNew)
{ case sheet::DataPilotFieldOrientation_COLUMN:
maColDims.push_back(nColumn); break; case sheet::DataPilotFieldOrientation_ROW:
maRowDims.push_back(nColumn); break; case sheet::DataPilotFieldOrientation_DATA:
maDataDims.push_back(nColumn); break; case sheet::DataPilotFieldOrientation_PAGE:
maPageDims.push_back(nColumn); break; // DataPilot Migration - Cache&&Performance case sheet::DataPilotFieldOrientation_HIDDEN: break; default:
OSL_FAIL( "ScDPSource::SetOrientation: unexpected orientation" ); break;
}
}
ScDPDimension* ScDPSource::AddDuplicated(std::u16string_view rNewName)
{
OSL_ENSURE(mpDimensions.is(), "AddDuplicated without dimensions?");
// re-use
tools::Long nOldDimCount = mpDimensions->getCount(); for (tools::Long i=0; i<nOldDimCount; i++)
{
ScDPDimension* pDim = mpDimensions->getByIndex(i); if (pDim && pDim->getName() == rNewName)
{ //TODO: test if pDim is a duplicate of source return pDim;
}
}
sal_Int32 ScDPSource::GetSourceDim(sal_Int32 nDim)
{ // original source dimension or data layout dimension? if (nDim <= mpData->GetColumnCount()) return nDim;
if (nDim < mpDimensions->getCount())
{
ScDPDimension* pDimObj = mpDimensions->getByIndex( nDim ); if ( pDimObj )
{
tools::Long nSource = pDimObj->GetSourceDim(); if ( nSource >= 0 ) return nSource;
}
}
maResFilterSet.swap(aFilterCxt.maFilterSet); // Keep this data for GETPIVOTDATA.
return aSeq;
}
uno::Sequence<double> ScDPSource::getFilteredResults( const uno::Sequence<sheet::DataPilotFieldFilter>& aFilters )
{ if (maResFilterSet.empty())
getResults(); // Build result tree first.
// Get result values from the tree. const ScDPResultTree::ValuesType* pVals = maResFilterSet.getResults(aFilters); if (pVals && !pVals->empty())
{ return comphelper::containerToSequence(*pVals);
}
if (aFilters.getLength() == 1)
{ // Try to get result from the leaf nodes. double fVal = maResFilterSet.getLeafResult(aFilters[0]); if (!std::isnan(fVal))
{ return { fVal };
}
}
// Take into account the visibilities of field members.
ScDPResultVisibilityData aResVisData(this);
mpRowResultRoot->FillVisibilityData(aResVisData);
mpColumnResultRoot->FillVisibilityData(aResVisData);
aResVisData.fillFieldFilters(aFilterCriteria);
static tools::Long lcl_CountMinMembers(const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLevel, tools::Long nLevels )
{ // Calculate the product of the member count for those consecutive levels that // have the "show all" flag, one following level, and the data layout dimension.
void ScDPSource::FilterCacheByPageDimensions()
{ // #i117661# Repeated calls to ScDPFilteredCache::filterByPageDimension // are invalid because rows are only hidden, never shown again. If // FilterCacheByPageDimensions is called again, the cache table must // be re-initialized. Currently, CreateRes_Impl always uses a fresh cache // because ScDBDocFunc::DataPilotUpdate calls InvalidateData.
if (mbPageFiltered)
{
SAL_WARN( "sc.core","tried to apply page field filters several times");
void ScDPSource::CreateRes_Impl()
{ if (mpResultData) return;
sheet::DataPilotFieldOrientation nDataOrient = GetDataLayoutOrientation(); if (maDataDims.size() > 1 && ( nDataOrient != sheet::DataPilotFieldOrientation_COLUMN &&
nDataOrient != sheet::DataPilotFieldOrientation_ROW ) )
{ // if more than one data dimension, data layout orientation must be set
SetOrientation(mpData->GetColumnCount(), sheet::DataPilotFieldOrientation_ROW);
nDataOrient = sheet::DataPilotFieldOrientation_ROW;
}
// TODO: Aggregate pDataNames, pDataRefValues, nDataRefOrient, and // eDataFunctions into a structure and use vector instead of static // or pointer arrays.
vector<OUString> aDataNames;
vector<sheet::DataPilotFieldReference> aDataRefValues;
vector<ScSubTotalFunc> aDataFunctions;
vector<sheet::DataPilotFieldOrientation> aDataRefOrient;
ScDPTableData::CalcInfo aInfo;
// LateInit (initialize only those rows/children that are used) can be used unless // any data dimension needs reference values from column/row dimensions bool bLateInit = true;
// Go through all data dimensions (i.e. fields) and build their meta data // so that they can be passed on to ScDPResultData instance later. // TODO: aggregate all of data dimension info into a structure. for (const tools::Long nDimIndex : maDataDims)
{ // Get function for each data field.
ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex);
ScGeneralFunction eUser = pDim->getFunction(); if (eUser == ScGeneralFunction::AUTO)
{ //TODO: test for numeric data
eUser = ScGeneralFunction::SUM;
}
// Map UNO's enum to internal enum ScSubTotalFunc.
aDataFunctions.push_back(ScDPUtil::toSubTotalFunc(eUser));
// Get reference field/item information.
aDataRefValues.push_back(pDim->GetReferenceValue());
sheet::DataPilotFieldOrientation nDataRefOrient = sheet::DataPilotFieldOrientation_HIDDEN; // default if not used
sal_Int32 eRefType = aDataRefValues.back().ReferenceType; if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ||
eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL )
{
sal_Int32 nColumn = comphelper::findValue(
GetDimensionsObject()->getElementNames(), aDataRefValues.back().ReferenceField); if ( nColumn >= 0 )
{
nDataRefOrient = GetOrientation(nColumn); // need fully initialized results to find reference values // (both in column or row dimensions), so updated values or // differences to 0 can be displayed even for empty results.
bLateInit = false;
}
}
aDataRefOrient.push_back(nDataRefOrient);
aDataNames.push_back(pDim->getName());
//TODO: modify user visible strings as in ScDPResultData::GetMeasureString instead!
// Page field selections restrict the members shown in related fields // (both in column and row fields). aInitState is filled with the page // field selections, they are kept across the data iterator loop.
for (constauto& rDimIndex : maPageDims)
{
ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex); if ( pDim->HasSelectedPage() )
aInitState.AddMember(rDimIndex, GetCache()->GetIdByItemData(rDimIndex, pDim->GetSelectedData()));
}
// Show grand total columns only when the option is set *and* there is at // least one column field. Same for the grand total rows.
sheet::DataPilotFieldOrientation nDataLayoutOrient = GetDataLayoutOrientation();
tools::Long nColDimCount2 = maColDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_COLUMN ? 1 : 0);
tools::Long nRowDimCount2 = maRowDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_ROW ? 1 : 0); bool bShowColGrand = mbColumnGrand && nColDimCount2 > 0; bool bShowRowGrand = mbRowGrand && nRowDimCount2 > 0;
mpColumnResultRoot.reset( new ScDPResultMember(mpResultData.get(), bShowColGrand) );
mpRowResultRoot.reset( new ScDPResultMember(mpResultData.get(), bShowRowGrand) );
if ( nMinColMembers > MAXCOLCOUNT/*SC_MINCOUNT_LIMIT*/ || nMinRowMembers > SC_MINCOUNT_LIMIT )
{ // resulting table is too big -> abort before calculating // (this relies on late init, so no members are allocated in InitFrom above)
// With all data processed, calculate the final results:
// UpdateDataResults calculates all original results from the collected values, // and stores them as reference values if needed.
mpRowResultRoot->UpdateDataResults(mpColumnResultRoot.get(), mpResultData->GetRowStartMeasure());
if ( bHasAutoShow ) // do the double calculation only if AutoShow is used
{ // Find the desired members and set bAutoHidden flag for the others
mpRowResultRoot->DoAutoShow(mpColumnResultRoot.get());
// Reset all results to empty, so they can be built again with data for the // desired members only.
mpColumnResultRoot->ResetResults();
mpRowResultRoot->ResetResults();
mpData->CalcResults(aInfo, true);
// Call UpdateDataResults again, with the new (limited) values.
mpRowResultRoot->UpdateDataResults(mpColumnResultRoot.get(), mpResultData->GetRowStartMeasure());
}
// SortMembers does the sorting by a result dimension, using the original results, // but not running totals etc.
mpRowResultRoot->SortMembers(mpColumnResultRoot.get());
// UpdateRunningTotals calculates running totals along column/row dimensions, // differences from other members (named or relative), and column/row percentages // or index values. // Running totals and relative differences need to be done using the sorted values. // Column/row percentages and index values must be done after sorting, because the // results may no longer be in the right order (row total for percentage of row is // always 1).
ScDPRunningTotalState aRunning(mpColumnResultRoot.get(), mpRowResultRoot.get());
ScDPRowTotals aTotals;
mpRowResultRoot->UpdateRunningTotals(mpColumnResultRoot.get(), mpResultData->GetRowStartMeasure(), aRunning, aTotals);
void ScDPSource::FillMemberResults()
{ if (mpColumnResults || mpRowResults) return;
CreateRes_Impl();
if (mbResultOverflow) // set in CreateRes_Impl
{ // no results available -> abort (leave empty) // exception is thrown in ScDPSource::getResults return;
}
const ScDPItemData& ScDPDimension::GetSelectedData()
{ if ( !pSelectedData )
{ // find the named member to initialize pSelectedData from it, with name and value
ScDPHierarchy* ScDPHierarchies::getByIndex(tools::Long nIndex) const
{ // pass hierarchy index to new object in case the implementation // will be extended to more than one hierarchy
if ( nIndex >= 0 && nIndex < nHierCount )
{ if ( !ppHiers )
{ const_cast<ScDPHierarchies*>(this)->ppHiers.reset( new rtl::Reference<ScDPHierarchy>[nHierCount] ); for (sal_Int32 i=0; i<nHierCount; i++)
ppHiers[i] = nullptr;
} if ( !ppHiers[nIndex].is() )
{
ppHiers[nIndex] = new ScDPHierarchy( pSource, nDim, nIndex );
}
void ScDPLevel::EvaluateSortOrder()
{ switch (aSortInfo.Mode)
{ case sheet::DataPilotFieldSortMode::DATA:
{ // find index of measure (index among data dimensions)
//TODO: error if not found?
} break; case sheet::DataPilotFieldSortMode::MANUAL: case sheet::DataPilotFieldSortMode::NAME:
{
ScDPMembers* pLocalMembers = GetMembersObject();
tools::Long nCount = pLocalMembers->getCount();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.