/* -*- 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 ::std::shared_ptr; using ::com::sun::star::uno::Sequence; using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::uno::Any; using ::com::sun::star::uno::Exception; using ::com::sun::star::lang::XComponent; using ::com::sun::star::sheet::DataPilotTableHeaderData; using ::com::sun::star::sheet::DataPilotTablePositionData; using ::com::sun::star::sheet::XDimensionsSupplier; using ::com::sun::star::beans::XPropertySet;
/** * Database connection implementation for UNO database API. Note that in * the UNO database API, column index is 1-based, whereas the interface * requires that column index be 0-based.
*/ class DBConnector : public ScDPCache::DBConnector
{
ScDPCache& mrCache;
uno::Reference<sdbc::XRowSet> mxRowSet;
uno::Reference<sdbc::XRow> mxRow;
uno::Reference<sdbc::XResultSetMetaData> mxMetaData;
Date maNullDate;
try
{ double fValue = 0.0; switch (nType)
{ case sdbc::DataType::BIT: case sdbc::DataType::BOOLEAN:
{
rNumType = SvNumFormatType::LOGICAL;
fValue = mxRow->getBoolean(nCol+1) ? 1 : 0;
rData.SetValue(fValue); break;
} case sdbc::DataType::TINYINT: case sdbc::DataType::SMALLINT: case sdbc::DataType::INTEGER: case sdbc::DataType::BIGINT: case sdbc::DataType::FLOAT: case sdbc::DataType::REAL: case sdbc::DataType::DOUBLE: case sdbc::DataType::NUMERIC: case sdbc::DataType::DECIMAL:
{ //TODO: do the conversion here?
fValue = mxRow->getDouble(nCol+1);
rData.SetValue(fValue); break;
} case sdbc::DataType::DATE:
{
rNumType = SvNumFormatType::DATE;
if (rOther.mpSaveData)
mpSaveData.reset(new ScDPSaveData(*rOther.mpSaveData)); if (rOther.mpSheetDescription)
mpSheetDescription.reset(new ScSheetSourceDesc(*rOther.mpSheetDescription)); if (rOther.mpImportDescription)
mpImportDescription.reset(new ScImportSourceDesc(*rOther.mpImportDescription)); if (rOther.mpServiceDescription)
mpServiceDescription.reset(new ScDPServiceDesc(*rOther.mpServiceDescription));
} return *this;
}
void ScDPObject::EnableGetPivotData(bool b)
{
mbEnableGetPivotData = b;
}
bool ScDPObject::IsDataDescriptionCell(const ScAddress& rPos)
{ if (!mpSaveData) returnfalse;
tools::Long nDataDimCount = mpSaveData->GetDataDimensionCount(); if (nDataDimCount != 1) // There has to be exactly one data dimension for the description to // appear at top-left corner. returnfalse;
voidoperator() (const std::unique_ptr<ScDPObject>& rObj)
{ const ScRange& rRange = rObj->GetOutRange(); if (mnTab != rRange.aStart.Tab()) // Not on this sheet. return;
if (mpImportDescription)
{ // database data const ScDPCache* pCache = mpImportDescription->CreateCache(pDimData); if (pCache)
{
pCache->AddReference(this);
pData = std::make_shared<ScDatabaseDPData>(mpDocument, *pCache);
}
} else
{ // cell data if (!mpSheetDescription)
{
OSL_FAIL("no source descriptor");
mpSheetDescription.reset(new ScSheetSourceDesc(mpDocument)); // dummy defaults
}
{ // Temporarily disable GETPIVOTDATA to avoid having // GETPIVOTDATA called onto itself from within the source // range.
DisableGetPivotData aSwitch(*this, mbEnableGetPivotData); const ScDPCache* pCache = mpSheetDescription->CreateCache(pDimData); if (pCache)
{
pCache->AddReference(this);
pData = std::make_shared<ScSheetDPData>(mpDocument, *mpSheetDescription, *pCache);
}
}
}
// grouping (for cell or database data) if (pData && pDimData)
{ auto pGroupData = std::make_shared<ScDPGroupTableData>(pData, mpDocument);
pDimData->WriteToData(*pGroupData);
pData = pGroupData;
}
mpTableData = std::move(pData); // after SetCacheId
}
return mpTableData.get();
}
void ScDPObject::CreateObjects()
{ if (!mxSource.is())
{
mpOutput.reset(); // not valid when mxSource is changed
if (mpServiceDescription)
{
mxSource = CreateSource(*mpServiceDescription);
}
if (!mxSource.is()) // database or sheet data, or error in CreateSource
{
OSL_ENSURE(!mpServiceDescription, "DPSource could not be created");
ScDPTableData* pData = GetTableData(); if (pData)
{ if (mpSaveData) // Make sure to transfer these flags to the table data // since they may have changed.
pData->SetEmptyFlags(mpSaveData->GetIgnoreEmptyRows(), mpSaveData->GetRepeatIfEmpty());
pData->ReloadCacheTable();
mxSource = new ScDPSource( pData );
}
}
if (mpSaveData)
mpSaveData->WriteToSource(mxSource);
} elseif (mbSettingsChanged)
{
mpOutput.reset(); // not valid when mxSource is changed
uno::Reference<util::XRefreshable> xRef(mxSource, uno::UNO_QUERY); if (xRef.is())
{ try
{
xRef->refresh();
} catch(uno::Exception&)
{
TOOLS_WARN_EXCEPTION( "sc", "exception in refresh");
}
}
if (mpSaveData)
mpSaveData->WriteToSource(mxSource);
}
mbSettingsChanged = false;
}
if (!mpTableData) // Table data not built yet. No need to reload the group data. return;
if (!mpSaveData) // How could it not have the save data... but whatever. return;
const ScDPDimensionSaveData* pDimData = mpSaveData->GetExistingDimensionData(); if (!pDimData || !pDimData->HasGroupDimensions())
{ // No group dimensions exist. Check if it currently has group // dimensions, and if so, remove all of them.
ScDPGroupTableData* pData = dynamic_cast<ScDPGroupTableData*>(mpTableData.get()); if (pData)
{ // Replace the existing group table data with the source data.
mpTableData = pData->GetSourceTableData();
} return;
}
ScDPGroupTableData* pData = dynamic_cast<ScDPGroupTableData*>(mpTableData.get()); if (pData)
{ // This is already a group table data. Salvage the source data and // re-create a new group data. const shared_ptr<ScDPTableData>& pSource = pData->GetSourceTableData(); auto pGroupData = std::make_shared<ScDPGroupTableData>(pSource, mpDocument);
pDimData->WriteToData(*pGroupData);
mpTableData = pGroupData;
} else
{ // This is a source data. Create a group data based on it. auto pGroupData = std::make_shared<ScDPGroupTableData>(mpTableData, mpDocument);
pDimData->WriteToData(*pGroupData);
mpTableData = pGroupData;
}
ScRange ScDPObject::GetNewOutputRange( bool& rOverflow )
{
CreateOutput(); // create mxSource and mpOutput if not already done
rOverflow = mpOutput->HasError(); // range overflow or exception from source if ( rOverflow ) return ScRange(maOutputRange.aStart); else
{ // don't store the result in maOutputRange, because nothing has been output yet return mpOutput->GetOutputRange();
}
}
CreateOutput(); // create mxSource and mpOutput if not already done
mpOutput->SetPosition( rPos );
mpOutput->Output();
// maOutputRange is always the range that was last output to the document
maOutputRange = mpOutput->GetOutputRange(); const ScAddress& s = maOutputRange.aStart; const ScAddress& e = maOutputRange.aEnd;
mpDocument->ApplyFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), ScMF::DpTable);
}
void ScDPObject::RefreshAfterLoad()
{ // apply drop-down attribute, initialize mnHeaderRows, without accessing the source // (button attribute must be present)
// simple test: block of button cells at the top, followed by an empty cell
bool ScDPObject::SyncAllDimensionMembers()
{ if (!mpSaveData) returnfalse;
// #i111857# don't always create empty mpTableData for external service. // Ideally, mxSource should be used instead of mpTableData. if (mpServiceDescription) returnfalse;
ScDPTableData* pData = GetTableData(); if (!pData) // No table data exists. This can happen when refreshing from an // external source which doesn't exist. returnfalse;
// Refresh the cache wrapper since the cache may have changed.
pData->SetEmptyFlags(mpSaveData->GetIgnoreEmptyRows(), mpSaveData->GetRepeatIfEmpty());
pData->ReloadCacheTable();
mpSaveData->SyncAllDimensionMembers(pData); returntrue;
}
const OUString& rRangeName = mpSheetDescription->GetRangeName(); if (!rRangeName.isEmpty()) // Source range is a named range. No need to update. return;
if (mpSheetDescription && rOther.mpSheetDescription)
{ if (mpSheetDescription->GetSourceRange() != rOther.mpSheetDescription->GetSourceRange()) returnfalse;
} elseif (mpSheetDescription || rOther.mpSheetDescription)
{
OSL_FAIL("RefsEqual: SheetDesc set at only one object"); returnfalse;
}
returntrue;
}
void ScDPObject::WriteRefsTo(ScDPObject& rObject) const
{
rObject.SetOutRange(maOutputRange); if (mpSheetDescription)
rObject.SetSheetDesc(*mpSheetDescription);
}
vector<sheet::DataPilotFieldFilter> aFilters; if (!mpOutput->GetDataResultPositionData(aFilters, rPos)) returnfalse;
sal_Int32 n = static_cast<sal_Int32>(aFilters.size());
rFilters.realloc(n); auto pFilters = rFilters.getArray(); for (sal_Int32 i = 0; i < n; ++i)
pFilters[i] = aFilters[i];
uno::Reference<sheet::XDataPilotResults> xDPResults(mxSource, uno::UNO_QUERY); if (!xDPResults.is()) return std::numeric_limits<double>::quiet_NaN();
// Dimensions must be sorted in order of appearance, and row dimensions // must come before column dimensions.
std::sort(rFilters.begin(), rFilters.end(), LessByDimOrder(mpSaveData->GetDimensionSortOrder()));
size_t n = rFilters.size();
uno::Sequence<sheet::DataPilotFieldFilter> aFilters(n); auto aFiltersRange = asNonConstRange(aFilters); for (size_t i = 0; i < n; ++i)
aFiltersRange[i] = rFilters[i];
uno::Sequence<double> aRes = xDPResults->getFilteredResults(aFilters); if (nDataIndex >= o3tl::make_unsigned(aRes.getLength())) return std::numeric_limits<double>::quiet_NaN();
return aRes[nDataIndex];
}
bool ScDPObject::IsFilterButton( const ScAddress& rPos )
{
CreateOutput(); // create mxSource and mpOutput if not already done
return mpOutput->IsFilterButton( rPos );
}
tools::Long ScDPObject::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient )
{
CreateOutput(); // create mxSource and mpOutput if not already done
return mpOutput->GetHeaderDim( rPos, rOrient );
}
bool ScDPObject::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop, tools::Long nDragDim,
tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos )
{
CreateOutput();// create mxSource and mpOutput if not already done
void ScDPObject::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension)
{
CreateOutput();// create mxSource and mpOutput if not already done
mpOutput->GetMemberResultNames(rNames, nDimension); // used only with table data -> level not needed
}
if (nStartPos < nListLen && rList[nStartPos] == '\'') // quoted within the brackets?
{ if ( dequote( rList, nStartPos, nQuoteEnd, aDequoted ) )
{ // after the quoted string, there must be the closing bracket, optionally preceded by spaces, // and/or a function name while (nQuoteEnd < nListLen && rList[nQuoteEnd] == ' ')
++nQuoteEnd;
// semicolon separates function name if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ';' && pFunc)
{
sal_Int32 nFuncEnd = 0; if ( parseFunction( rList, nQuoteEnd + 1, nFuncEnd, *pFunc ) )
nQuoteEnd = nFuncEnd;
} if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ']')
{
++nQuoteEnd; // include the closing bracket for the matched length
bParsed = true;
}
}
} else
{ // implicit quoting to the closing bracket
// field name has to be followed by item name in brackets if (aRemaining.startsWith("["))
{
bHasFieldName = true; // bUsed remains false - still need the item
} else
{
bUsed = true;
bError = true;
}
}
}
}
OUString aQueryValue = aQueryValueName; if (mpTableData)
{
ScInterpreterContext& rContext = mpTableData->GetCacheTable().getCache().GetInterpreterContext(); // Parse possible number from aQueryValueName and format // locale independent as aQueryValue.
sal_uInt32 nNumFormat = 0; double fValue; if (rContext.NFIsNumberFormat(aQueryValueName, nNumFormat, fValue))
aQueryValue = ScDPCache::GetLocaleIndependentFormattedString(fValue, rContext, nNumFormat);
}
for ( SCSIZE nField=0; nField<nFieldCount; nField++ )
{ // If a field name is given, look in that field only, otherwise in all fields. // aSpecField is initialized from aFieldNames array, so exact comparison can be used. if ( !bHasFieldName || aFieldNames[nField] == aSpecField )
{ const uno::Sequence<OUString>& rItemNames = aFieldValueNames[nField]; const uno::Sequence<OUString>& rItemValues = aFieldValues[nField];
sal_Int32 nItemCount = rItemNames.getLength();
assert(nItemCount == rItemValues.getLength()); const OUString* pItemNamesArr = rItemNames.getConstArray(); const OUString* pItemValuesArr = rItemValues.getConstArray(); for ( sal_Int32 nItem=0; nItem<nItemCount; nItem++ )
{ bool bThisItemFound; if (bHasQuery)
{ // First check given value name against both.
bThisItemFound = ScGlobal::GetTransliteration().isEqual(
aQueryValueName, pItemNamesArr[nItem]); if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
bThisItemFound = ScGlobal::GetTransliteration().isEqual(
aQueryValueName, pItemValuesArr[nItem]); if (!bThisItemFound && aQueryValueName != aQueryValue)
{ // Second check locale independent value // against both. /* TODO: or check only value string against
* value string, not against the value name? */
bThisItemFound = ScGlobal::GetTransliteration().isEqual(
aQueryValue, pItemNamesArr[nItem]); if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
bThisItemFound = ScGlobal::GetTransliteration().isEqual(
aQueryValue, pItemValuesArr[nItem]);
}
} else
{
bThisItemFound = isAtStart( aRemaining, pItemNamesArr[nItem], nMatched, false, &eFunc ); if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
bThisItemFound = isAtStart( aRemaining, pItemValuesArr[nItem], nMatched, false, &eFunc ); /* TODO: this checks only the given value name, * check also locale independent value. But we'd * have to do that in each iteration of the loop * inside isAtStart() since a query could not be * extracted and a match could be on the passed * item value name string or item value string
* starting at aRemaining. */
} if (bThisItemFound)
{ if ( bItemFound )
bError = true; // duplicate (also across fields) else
{
aFoundName = aFieldNames[nField];
aFoundValueName = pItemNamesArr[nItem];
aFoundValue = pItemValuesArr[nItem];
eFoundFunc = eFunc;
bItemFound = true;
bUsed = true;
}
}
}
}
}
// remove any number of spaces between entries
aRemaining = comphelper::string::stripStart(aRemaining, ' ');
}
if ( !bError && !bHasData && aDataNames.size() == 1 )
{ // if there's only one data field, its name need not be specified
rDataFieldName = aDataNames[0];
bHasData = true;
}
return bHasData && !bError;
}
void ScDPObject::ToggleDetails(const DataPilotTableHeaderData& rElemDesc, ScDPObject* pDestObj)
{
CreateObjects(); // create mxSource if not already done
uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
SC_UNO_DP_ISDATALAYOUT ); if (bDataLayout)
{ // the elements of the data layout dimension can't be found by their names // -> don't change anything return;
}
if ( xDimProp.is() && nDimOrient == nOrient )
{ // Let's take this dimension.
// function mask.
PivotFunc nMask = PivotFunc::NONE; if ( nOrient == sheet::DataPilotFieldOrientation_DATA )
{
sal_Int16 eFunc = ScUnoHelpFunctions::GetShortProperty(
xDimProp, SC_UNO_DP_FUNCTION2,
sheet::GeneralFunction2::NONE ); if ( eFunc == sheet::GeneralFunction2::AUTO )
{ //TODO: test for numeric data
eFunc = sheet::GeneralFunction2::SUM;
}
nMask = ScDataPilotConversion::FunctionBit(eFunc);
} else
nMask = lcl_FirstSubTotal( xDimProp ); // from first hierarchy
// is this data layout dimension? bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty(
xDimProp, SC_UNO_DP_ISDATALAYOUT);
// is this dimension cloned?
tools::Long nDupSource = -1; try
{
uno::Any aOrigAny = xDimProp->getPropertyValue(SC_UNO_DP_ORIGINAL_POS);
sal_Int32 nTmp = 0; if (aOrigAny >>= nTmp)
nDupSource = nTmp;
} catch(uno::Exception&)
{
}
sal_uInt8 nDupCount = 0; if (nDupSource >= 0)
{ // this dimension is cloned.
SCCOL nCompCol; // ID of the original dimension. if ( bDataLayout )
nCompCol = PIVOT_DATA_FIELD; else
nCompCol = static_cast<SCCOL>(nDupSource); //TODO: seek source column from name
ScPivotFieldVector::iterator it = std::find_if(aFields.begin(), aFields.end(), FindByColumn(nCompCol, nMask)); if (it != aFields.end())
nDupCount = it->mnDupCount + 1;
}
aFields.emplace_back();
ScPivotField& rField = aFields.back(); if (bDataLayout)
{
rField.nCol = PIVOT_DATA_FIELD;
bDataFound = true;
} else
{
rField.mnOriginalDim = nDupSource;
rField.nCol = static_cast<SCCOL>(nDim); //TODO: seek source column from name
}
ScDPSaveDimension* pDim = nullptr; if ( nCol == PIVOT_DATA_FIELD )
pDim = rSaveData.GetDataLayoutDimension(); else
{
OUString aDocStr = lcl_GetDimName( xSource, nCol ); // cols must start at 0 if (!aDocStr.isEmpty())
pDim = rSaveData.GetDimensionByName(aDocStr); else
pDim = nullptr;
}
if (!pDim) continue;
if ( nOrient == sheet::DataPilotFieldOrientation_DATA ) // set summary function
{ // generate an individual entry for each function bool bFirst = true;
// if a dimension is used for column/row/page and data, // use duplicated dimensions for all data occurrences if (hasFieldColumn(pRefColFields, nCol))
bFirst = false;
if (bFirst && hasFieldColumn(pRefRowFields, nCol))
bFirst = false;
if (bFirst && hasFieldColumn(pRefPageFields, nCol))
bFirst = false;
if (bFirst)
{ // if set via api, a data column may occur several times // (if the function hasn't been changed yet) -> also look for duplicate data column
bFirst = std::none_of(itrBeg, itr, FindByOriginalDim(nCol));
}
// ShowEmpty was implicit in old tables, // must be set for data layout dimension (not accessible in dialog) if ( nCol == PIVOT_DATA_FIELD )
pDim->SetShowEmpty( true );
}
size_t nDimIndex = rField.nCol;
pDim->RemoveLayoutName();
pDim->RemoveSubtotalName(); if (nDimIndex < rLabels.size())
{ const ScDPLabelData& rLabel = *rLabels[nDimIndex]; if (!rLabel.maLayoutName.isEmpty())
pDim->SetLayoutName(rLabel.maLayoutName); if (!rLabel.maSubtotalName.isEmpty())
pDim->SetSubtotalName(rLabel.maSubtotalName);
}
}
}
uno::Reference<container::XEnumeration> xEnum =
xEnAc->createContentEnumeration(SCDPSOURCE_SERVICE); if (!xEnum.is()) return xRet;
while (xEnum->hasMoreElements() && !xRet.is())
{
uno::Any aAddInAny = xEnum->nextElement();
uno::Reference<uno::XInterface> xIntFac;
aAddInAny >>= xIntFac; if (!xIntFac.is()) continue;
uno::Reference<lang::XServiceInfo> xInfo(xIntFac, uno::UNO_QUERY); if (!xInfo.is() || xInfo->getImplementationName() != aImplName) continue;
try
{ // #i113160# try XSingleComponentFactory in addition to (old) XSingleServiceFactory, // passing the context to the component (see ScUnoAddInCollection::Initialize)
void setGroupItemsToCache( ScDPCache& rCache, const o3tl::sorted_vector<ScDPObject*>& rRefs )
{ // Go through all referencing pivot tables, and re-fill the group dimension info. for (const ScDPObject* pObj : rRefs)
{ const ScDPSaveData* pSave = pObj->GetSaveData(); if (!pSave) continue;
const ScDPDimensionSaveData* pGroupDims = pSave->GetExistingDimensionData(); if (!pGroupDims) continue;
pGroupDims->WriteToCache(rCache);
}
}
}
bool ScDPCollection::SheetCaches::hasCache(const ScRange& rRange) const
{
RangeIndexType::const_iterator it = std::find(maRanges.begin(), maRanges.end(), rRange); if (it == maRanges.end()) returnfalse;
const ScDPCache* ScDPCollection::SheetCaches::getCache(const ScRange& rRange, const ScDPDimensionSaveData* pDimData)
{
RangeIndexType::iterator it = std::find(maRanges.begin(), maRanges.end(), rRange); if (it != maRanges.end())
{ // Already cached.
size_t nIndex = std::distance(maRanges.begin(), it);
CachesType::iterator const itCache = m_Caches.find(nIndex); if (itCache == m_Caches.end())
{
OSL_FAIL("Cache pool and index pool out-of-sync !!!"); return nullptr;
}
if (pDimData)
{
(itCache->second)->ClearGroupFields();
pDimData->WriteToCache(*itCache->second);
}
return itCache->second.get();
}
// Not cached. Create a new cache.
::std::unique_ptr<ScDPCache> pCache(new ScDPCache(mrDoc));
pCache->InitFromDoc(mrDoc, rRange); if (pDimData)
pDimData->WriteToCache(*pCache);
// Get the smallest available range index.
it = std::find_if(maRanges.begin(), maRanges.end(), FindInvalidRange());
size_t nIndex = maRanges.size(); if (it == maRanges.end())
{ // All range indices are valid. Append a new index.
maRanges.push_back(rRange);
} else
{ // Slot with invalid range. Re-use this slot.
*it = rRange;
nIndex = std::distance(maRanges.begin(), it);
}
const ScDPCache* p = pCache.get();
m_Caches.insert(std::make_pair(nIndex, std::move(pCache))); return p;
}
ScDPCache* ScDPCollection::SheetCaches::getExistingCache(const ScRange& rRange)
{
RangeIndexType::iterator it = std::find(maRanges.begin(), maRanges.end(), rRange); if (it == maRanges.end()) // Not cached. return nullptr;
// Already cached.
size_t nIndex = std::distance(maRanges.begin(), it);
CachesType::iterator const itCache = m_Caches.find(nIndex); if (itCache == m_Caches.end())
{
OSL_FAIL("Cache pool and index pool out-of-sync !!!"); return nullptr;
}
return itCache->second.get();
}
const ScDPCache* ScDPCollection::SheetCaches::getExistingCache(const ScRange& rRange) const
{
RangeIndexType::const_iterator it = std::find(maRanges.begin(), maRanges.end(), rRange); if (it == maRanges.end()) // Not cached. return nullptr;
// Already cached.
size_t nIndex = std::distance(maRanges.begin(), it);
CachesType::const_iterator const itCache = m_Caches.find(nIndex); if (itCache == m_Caches.end())
{
OSL_FAIL("Cache pool and index pool out-of-sync !!!"); return nullptr;
}
void ScDPCollection::SheetCaches::updateCache(const ScRange& rRange, o3tl::sorted_vector<ScDPObject*>& rRefs)
{
RangeIndexType::iterator it = std::find(maRanges.begin(), maRanges.end(), rRange); if (it == maRanges.end())
{ // Not cached. Nothing to do.
rRefs.clear(); return;
}
size_t nIndex = std::distance(maRanges.begin(), it);
CachesType::iterator const itCache = m_Caches.find(nIndex); if (itCache == m_Caches.end())
{
OSL_FAIL("Cache pool and index pool out-of-sync !!!");
rRefs.clear(); return;
}
ScDPCache& rCache = *itCache->second;
// Update the cache with new cell values. This will clear all group dimension info.
rCache.InitFromDoc(mrDoc, rRange);
ScDPCache& rCache = *itr->second; // Update the cache with new cell values. This will clear all group dimension info.
rCache.InitFromDoc(mrDoc, rRange);
/** * Unary predicate to match DP objects by the table ID.
*/ class MatchByTable
{
SCTAB mnTab; public: explicit MatchByTable(SCTAB nTab) : mnTab(nTab) {}
if (pDPObj->IsSheetData())
{ // data source is internal sheet. const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc(); if (!pDesc) return STR_ERR_DATAPILOTSOURCE;
TranslateId pErrId = pDesc->CheckSourceRange(); if (pErrId) return pErrId;
if (pDesc->HasRangeName())
{ // cache by named range
ScDPCollection::NameCaches& rCaches = GetNameCaches(); if (rCaches.hasCache(pDesc->GetRangeName()))
rCaches.updateCache(pDesc->GetRangeName(), pDesc->GetSourceRange(), rRefs); else
{ // Not cached yet. Collect all tables that use this named // range as data source.
GetAllTables(pDesc->GetRangeName(), rRefs);
}
} else
{ // cache by cell range
ScDPCollection::SheetCaches& rCaches = GetSheetCaches(); if (rCaches.hasCache(pDesc->GetSourceRange()))
rCaches.updateCache(pDesc->GetSourceRange(), rRefs); else
{ // Not cached yet. Collect all tables that use this range as // data source.
GetAllTables(pDesc->GetSourceRange(), rRefs);
}
}
} elseif (pDPObj->IsImportData())
{ // data source is external database. const ScImportSourceDesc* pDesc = pDPObj->GetImportSourceDesc(); if (!pDesc) return STR_ERR_DATAPILOTSOURCE;
ScDPCollection::DBCaches& rCaches = GetDBCaches(); if (rCaches.hasCache(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject))
rCaches.updateCache(
pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, rRefs); else
{ // Not cached yet. Collect all tables that use this range as // data source.
GetAllTables(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, rRefs);
}
} return {};
}
bool ScDPCollection::ReloadGroupsInCache(const ScDPObject* pDPObj, o3tl::sorted_vector<ScDPObject*>& rRefs)
{ if (!pDPObj) returnfalse;
const ScDPSaveData* pSaveData = pDPObj->GetSaveData(); if (!pSaveData) returnfalse;
// Note: Unlike reloading cache, when modifying the group dimensions the // cache may not have all its references when this method is called. // Therefore, we need to always call GetAllTables to get its correct // references even when the cache exists. This may become a non-issue // if/when we implement loading and saving of pivot caches.
ScDPCache* pCache = nullptr;
if (pDPObj->IsSheetData())
{ // data source is internal sheet. const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc(); if (!pDesc) returnfalse;
if (pDesc->HasRangeName())
{ // cache by named range
ScDPCollection::NameCaches& rCaches = GetNameCaches(); if (rCaches.hasCache(pDesc->GetRangeName()))
pCache = rCaches.getExistingCache(pDesc->GetRangeName()); else
{ // Not cached yet. Cache the source dimensions. Groups will // be added below.
pCache = const_cast<ScDPCache*>(
rCaches.getCache(pDesc->GetRangeName(), pDesc->GetSourceRange(), nullptr));
}
GetAllTables(pDesc->GetRangeName(), rRefs);
} else
{ // cache by cell range
ScDPCollection::SheetCaches& rCaches = GetSheetCaches(); if (rCaches.hasCache(pDesc->GetSourceRange()))
pCache = rCaches.getExistingCache(pDesc->GetSourceRange()); else
{ // Not cached yet. Cache the source dimensions. Groups will // be added below.
pCache = const_cast<ScDPCache*>(
rCaches.getCache(pDesc->GetSourceRange(), nullptr));
}
GetAllTables(pDesc->GetSourceRange(), rRefs);
}
} elseif (pDPObj->IsImportData())
{ // data source is external database. const ScImportSourceDesc* pDesc = pDPObj->GetImportSourceDesc(); if (!pDesc) returnfalse;
ScDPCollection::DBCaches& rCaches = GetDBCaches(); if (rCaches.hasCache(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject))
pCache = rCaches.getExistingCache(
pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject); else
{ // Not cached yet. Cache the source dimensions. Groups will // be added below.
pCache = const_cast<ScDPCache*>(
rCaches.getCache(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, nullptr));
}
GetAllTables(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, rRefs);
}
if (!pCache) returnfalse;
// Clear the existing group/field data from the cache, and rebuild it from the // dimension data.
pCache->ClearAllFields(); const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData(); if (pDimData)
pDimData->WriteToCache(*pCache); returntrue;
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.67Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-05-07)
¤
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.