diff options
-rw-r--r-- | include/oox/core/relations.hxx | 3 | ||||
-rw-r--r-- | sc/Library_scfilt.mk | 1 | ||||
-rw-r--r-- | sc/inc/dpcache.hxx | 1 | ||||
-rw-r--r-- | sc/inc/dpobject.hxx | 4 | ||||
-rw-r--r-- | sc/inc/dpoutput.hxx | 3 | ||||
-rw-r--r-- | sc/source/core/data/dpcache.cxx | 8 | ||||
-rw-r--r-- | sc/source/core/data/dpobject.cxx | 32 | ||||
-rw-r--r-- | sc/source/core/data/dpoutput.cxx | 21 | ||||
-rw-r--r-- | sc/source/filter/excel/excdoc.cxx | 20 | ||||
-rw-r--r-- | sc/source/filter/excel/xepivotxml.cxx | 641 | ||||
-rw-r--r-- | sc/source/filter/excel/xeroot.cxx | 9 | ||||
-rw-r--r-- | sc/source/filter/inc/xepivotxml.hxx | 95 | ||||
-rw-r--r-- | sc/source/filter/inc/xeroot.hxx | 5 |
13 files changed, 833 insertions, 10 deletions
diff --git a/include/oox/core/relations.hxx b/include/oox/core/relations.hxx index bbe296883ba8..38a99c81f9e3 100644 --- a/include/oox/core/relations.hxx +++ b/include/oox/core/relations.hxx @@ -50,7 +50,8 @@ namespace core { #define CREATE_MSOFFICE_RELATION_TYPE( ascii ) \ ( "http://schemas.microsoft.com/office/2006/relationships/" ascii ) - +#define CREATE_XL_CONTENT_TYPE( ascii ) \ + ( "application/vnd.openxmlformats-officedocument.spreadsheetml." ascii "+xml" ) struct Relation { diff --git a/sc/Library_scfilt.mk b/sc/Library_scfilt.mk index f01e317b1bfc..00d32daa1923 100644 --- a/sc/Library_scfilt.mk +++ b/sc/Library_scfilt.mk @@ -96,6 +96,7 @@ $(eval $(call gb_Library_add_exception_objects,scfilt,\ sc/source/filter/excel/xename \ sc/source/filter/excel/xepage \ sc/source/filter/excel/xepivot \ + sc/source/filter/excel/xepivotxml \ sc/source/filter/excel/xerecord \ sc/source/filter/excel/xeroot \ sc/source/filter/excel/xestream \ diff --git a/sc/inc/dpcache.hxx b/sc/inc/dpcache.hxx index 63242435a7db..ecb35145d59a 100644 --- a/sc/inc/dpcache.hxx +++ b/sc/inc/dpcache.hxx @@ -156,6 +156,7 @@ public: long GetDimMemberCount(long nDim) const; SCROW GetOrder( long nDim, SCROW nIndex ) const; + const IndexArrayType* GetFieldIndexArray( size_t nDim ) const; const ItemsType& GetDimMemberValues( SCCOL nDim ) const; bool InitFromDoc(ScDocument* pDoc, const ScRange& rRange); bool InitFromDataBase(DBConnector& rDB); diff --git a/sc/inc/dpobject.hxx b/sc/inc/dpobject.hxx index 1ff70249ae16..46e20001be0b 100644 --- a/sc/inc/dpobject.hxx +++ b/sc/inc/dpobject.hxx @@ -132,6 +132,7 @@ public: ScRange GetNewOutputRange( bool& rOverflow ); ScRange GetOutputRangeByType( sal_Int32 nType ); + ScRange GetOutputRangeByType( sal_Int32 nType ) const; void SetSaveData(const ScDPSaveData& rData); ScDPSaveData* GetSaveData() const { return pSaveData; } @@ -287,9 +288,12 @@ public: UpdateRefMode eMode, const ScRange& r, SCsCOL nDx, SCsROW nDy, SCsTAB nDz); SC_DLLPUBLIC ScDPCache* getExistingCache(const ScRange& rRange); + SC_DLLPUBLIC const ScDPCache* getExistingCache(const ScRange& rRange) const; void updateCache(const ScRange& rRange, std::set<ScDPObject*>& rRefs); bool remove(const ScDPCache* p); + + SC_DLLPUBLIC const std::vector<ScRange>& getAllRanges() const; }; /** diff --git a/sc/inc/dpoutput.hxx b/sc/inc/dpoutput.hxx index 2127d37d7e4a..440f097bba8c 100644 --- a/sc/inc/dpoutput.hxx +++ b/sc/inc/dpoutput.hxx @@ -112,7 +112,8 @@ public: void SetPosition( const ScAddress& rPos ); void Output(); //! Refresh? - ScRange GetOutputRange( sal_Int32 nRegionType = ::com::sun::star::sheet::DataPilotOutputRangeType::WHOLE ); + ScRange GetOutputRange( sal_Int32 nRegionType = ::com::sun::star::sheet::DataPilotOutputRangeType::WHOLE ); + ScRange GetOutputRange( sal_Int32 nRegionType = ::com::sun::star::sheet::DataPilotOutputRangeType::WHOLE ) const; long GetHeaderRows(); bool HasError(); // range overflow or exception from source diff --git a/sc/source/core/data/dpcache.cxx b/sc/source/core/data/dpcache.cxx index e9687c5f409f..2dc66d3f8131 100644 --- a/sc/source/core/data/dpcache.cxx +++ b/sc/source/core/data/dpcache.cxx @@ -850,6 +850,14 @@ SCROW ScDPCache::GetDataSize() const return mnDataSize >= 0 ? mnDataSize : 0; } +const ScDPCache::IndexArrayType* ScDPCache::GetFieldIndexArray( size_t nDim ) const +{ + if (nDim >= maFields.size()) + return NULL; + + return &maFields[nDim].maData; +} + const ScDPCache::ItemsType& ScDPCache::GetDimMemberValues(SCCOL nDim) const { OSL_ENSURE( nDim>=0 && nDim < mnColumnCount ," nDim < mnColumnCount "); diff --git a/sc/source/core/data/dpobject.cxx b/sc/source/core/data/dpobject.cxx index 458720998cf1..387747c860f6 100644 --- a/sc/source/core/data/dpobject.cxx +++ b/sc/source/core/data/dpobject.cxx @@ -936,6 +936,14 @@ ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType ) return pOutput->GetOutputRange(nType); } +ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType ) const +{ + if (!pOutput || pOutput->HasError()) + return ScRange(ScAddress::INITIALIZE_INVALID); + + return pOutput->GetOutputRange(nType); +} + static bool lcl_HasButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab ) { return ((const ScMergeFlagAttr*)pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG ))->HasPivotButton(); @@ -2905,6 +2913,25 @@ ScDPCache* ScDPCollection::SheetCaches::getExistingCache(const ScRange& rRange) return itCache->second; } +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 NULL; + + // Already cached. + size_t nIndex = std::distance(maRanges.begin(), it); + CachesType::const_iterator itCache = maCaches.find(nIndex); + if (itCache == maCaches.end()) + { + OSL_FAIL("Cache pool and index pool out-of-sync !!!"); + return NULL; + } + + return itCache->second; +} + size_t ScDPCollection::SheetCaches::size() const { return maCaches.size(); @@ -2990,6 +3017,11 @@ bool ScDPCollection::SheetCaches::remove(const ScDPCache* p) return false; } +const std::vector<ScRange>& ScDPCollection::SheetCaches::getAllRanges() const +{ + return maRanges; +} + ScDPCollection::NameCaches::NameCaches(ScDocument* pDoc) : mpDoc(pDoc) {} bool ScDPCollection::NameCaches::hasCache(const OUString& rName) const diff --git a/sc/source/core/data/dpoutput.cxx b/sc/source/core/data/dpoutput.cxx index a23110bf73f8..eb5103502234 100644 --- a/sc/source/core/data/dpoutput.cxx +++ b/sc/source/core/data/dpoutput.cxx @@ -1166,6 +1166,27 @@ ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType ) return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab); } +ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType ) const +{ + using namespace ::com::sun::star::sheet; + + if (!bSizesValid) + return ScRange(ScAddress::INITIALIZE_INVALID); + + SCTAB nTab = aStartPos.Tab(); + switch (nRegionType) + { + case DataPilotOutputRangeType::RESULT: + return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab); + case DataPilotOutputRangeType::TABLE: + return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab); + default: + OSL_ENSURE(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type"); + break; + } + return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab); +} + bool ScDPOutput::HasError() { CalcSizes(); diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx index 5cb48d277b04..488f3cc28cc5 100644 --- a/sc/source/filter/excel/excdoc.cxx +++ b/sc/source/filter/excel/excdoc.cxx @@ -68,6 +68,7 @@ #include "xeescher.hxx" #include "xepivot.hxx" #include "XclExpChangeTrack.hxx" +#include <xepivotxml.hxx> #include <math.h> @@ -405,10 +406,6 @@ void ExcTable::FillAsHeaderXml( ExcBoundsheetList& rBoundsheetList ) // Formatting: FONT, FORMAT, XF, STYLE, PALETTE aRecList.AppendNewRecord( new XclExpXmlStyleSheet( *this ) ); - // Pivot Cache - GetPivotTableManager().CreatePivotTables(); - aRecList.AppendRecord( GetPivotTableManager().CreatePivotCachesRecord() ); - // Change tracking if( rDoc.GetChangeTrack() ) { @@ -641,10 +638,6 @@ void ExcTable::FillAsTableXml() // cell table: DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records aRecList.AppendRecord( mxCellTable ); - // pivot tables - // not in the worksheet file - aRecList.AppendRecord( GetPivotTableManager().CreatePivotTablesRecord( mnScTab ) ); - // list of NOTE records, generated by the cell table // not in the worksheet file if( mxNoteList != 0 && !mxNoteList->IsEmpty() ) @@ -742,6 +735,10 @@ void ExcTable::WriteXml( XclExpXmlStream& rStrm ) mxCellTable->Finalize(); aRecList.SaveXml( rStrm ); + XclExpXmlPivotTables* pPT = GetXmlPivotTableManager().GetTablesBySheet(mnScTab); + if (pPT) + pPT->SaveXml(rStrm); + rStrm.GetCurrentStream()->endElement( XML_worksheet ); rStrm.PopStream(); } @@ -766,7 +763,10 @@ void ExcDocument::ReadDoc( void ) if (GetOutput() == EXC_OUTPUT_BINARY) aHeader.FillAsHeaderBinary(maBoundsheetList); else + { aHeader.FillAsHeaderXml(maBoundsheetList); + GetXmlPivotTableManager().Initialize(); + } SCTAB nScTab = 0, nScTabCount = GetTabInfo().GetScTabCount(); SCTAB nCodeNameIdx = 0, nCodeNameCount = GetExtDocOptions().GetCodeNameCount(); @@ -873,6 +873,10 @@ void ExcDocument::WriteXml( XclExpXmlStream& rStrm ) if( pExpChangeTrack ) pExpChangeTrack->WriteXml( rStrm ); + XclExpXmlPivotCaches& rCaches = GetXmlPivotTableManager().GetCaches(); + if (rCaches.HasCaches()) + rCaches.SaveXml(rStrm); + rWorkbook->endElement( XML_workbook ); rWorkbook.reset(); diff --git a/sc/source/filter/excel/xepivotxml.cxx b/sc/source/filter/excel/xepivotxml.cxx new file mode 100644 index 000000000000..cef5a853ecfe --- /dev/null +++ b/sc/source/filter/excel/xepivotxml.cxx @@ -0,0 +1,641 @@ +/* -*- 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/. + */ + +#include <xepivotxml.hxx> +#include <dpcache.hxx> +#include <dpobject.hxx> +#include <dpsave.hxx> +#include <dputil.hxx> +#include <document.hxx> + +#include <oox/export/utils.hxx> + +#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> +#include <com/sun/star/sheet/DataPilotOutputRangeType.hpp> + +#include <vector> + +using namespace oox; +using namespace com::sun::star; + +namespace { + +void savePivotCacheRecordsXml( XclExpXmlStream& rStrm, const ScDPCache& rCache ) +{ + SCROW nCount = rCache.GetDataSize(); + size_t nFieldCount = rCache.GetFieldCount(); + + sax_fastparser::FSHelperPtr& pRecStrm = rStrm.GetCurrentStream(); + pRecStrm->startElement(XML_pivotCacheRecords, + XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main", + FSNS(XML_xmlns, XML_r), "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + XML_count, OString::number(static_cast<long>(nCount)).getStr(), + FSEND); + + for (SCROW i = 0; i < nCount; ++i) + { + pRecStrm->startElement(XML_r, FSEND); + for (size_t nField = 0; nField < nFieldCount; ++nField) + { + const ScDPCache::IndexArrayType* pArray = rCache.GetFieldIndexArray(nField); + assert(pArray); + assert(static_cast<size_t>(i) < pArray->size()); + pRecStrm->singleElement(XML_x, XML_v, OString::number((*pArray)[i]), FSEND); + } + pRecStrm->endElement(XML_r); + } + + pRecStrm->endElement(XML_pivotCacheRecords); +} + +const char* toOOXMLAxisType( sheet::DataPilotFieldOrientation eOrient ) +{ + switch (eOrient) + { + case sheet::DataPilotFieldOrientation_COLUMN: + return "axisCol"; + case sheet::DataPilotFieldOrientation_ROW: + return "axisRow"; + case sheet::DataPilotFieldOrientation_PAGE: + return "axisPage"; + case sheet::DataPilotFieldOrientation_DATA: + return "axisValues"; + case sheet::DataPilotFieldOrientation_HIDDEN: + default: + ; + } + + return ""; +} + +} + +XclExpXmlPivotCaches::XclExpXmlPivotCaches( const XclExpRoot& rRoot ) : + XclExpRoot(rRoot) {} + +void XclExpXmlPivotCaches::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& pWorkbookStrm = rStrm.GetCurrentStream(); + pWorkbookStrm->startElement(XML_pivotCaches, FSEND); + + for (size_t i = 0, n = maCaches.size(); i < n; ++i) + { + const Entry& rEntry = maCaches[i]; + + sal_Int32 nCacheId = i + 1; + OUString aRelId; + sax_fastparser::FSHelperPtr pPCStrm = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheDefinition", nCacheId), + XclXmlUtils::GetStreamName(NULL, "pivotCache/pivotCacheDefinition", nCacheId), + rStrm.GetCurrentStream()->getOutputStream(), + CREATE_XL_CONTENT_TYPE("pivotCacheDefinition"), + CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"), + &aRelId); + + pWorkbookStrm->singleElement(XML_pivotCache, + XML_cacheId, OString::number(nCacheId).getStr(), + FSNS(XML_r, XML_id), XclXmlUtils::ToOString(aRelId).getStr(), + FSEND); + + rStrm.PushStream(pPCStrm); + SavePivotCacheXml(rStrm, rEntry, nCacheId); + rStrm.PopStream(); + } + + pWorkbookStrm->endElement(XML_pivotCaches); +} + +void XclExpXmlPivotCaches::SetCaches( const std::vector<Entry>& rCaches ) +{ + maCaches = rCaches; +} + +bool XclExpXmlPivotCaches::HasCaches() const +{ + return !maCaches.empty(); +} + +const XclExpXmlPivotCaches::Entry* XclExpXmlPivotCaches::GetCache( sal_Int32 nCacheId ) const +{ + if (nCacheId <= 0) + // cache ID is 1-based. + return NULL; + + size_t nPos = nCacheId - 1; + if (nPos >= maCaches.size()) + return NULL; + + return &maCaches[nPos]; +} + +void XclExpXmlPivotCaches::SavePivotCacheXml( XclExpXmlStream& rStrm, const Entry& rEntry, sal_Int32 nCounter ) +{ + assert(rEntry.mpCache); + const ScDPCache& rCache = *rEntry.mpCache; + + sax_fastparser::FSHelperPtr& pDefStrm = rStrm.GetCurrentStream(); + + OUString aRelId; + sax_fastparser::FSHelperPtr pRecStrm = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheRecords", nCounter), + XclXmlUtils::GetStreamName(NULL, "pivotCacheRecords", nCounter), + pDefStrm->getOutputStream(), + CREATE_XL_CONTENT_TYPE("pivotCacheRecords"), + CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheRecords"), + &aRelId); + + rStrm.PushStream(pRecStrm); + savePivotCacheRecordsXml(rStrm, rCache); + rStrm.PopStream(); + + pDefStrm->startElement(XML_pivotCacheDefinition, + XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main", + FSNS(XML_xmlns, XML_r), "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + FSNS(XML_r, XML_id), XclXmlUtils::ToOString(aRelId).getStr(), + XML_recordCount, OString::number(rEntry.mpCache->GetDataSize()).getStr(), + FSEND); + + if (rEntry.meType == Worksheet) + { + pDefStrm->startElement(XML_cacheSource, + XML_type, "worksheet", + FSEND); + + OUString aSheetName; + GetDoc().GetName(rEntry.maSrcRange.aStart.Tab(), aSheetName); + pDefStrm->singleElement(XML_worksheetSource, + XML_ref, XclXmlUtils::ToOString(rEntry.maSrcRange).getStr(), + XML_sheet, XclXmlUtils::ToOString(aSheetName).getStr(), + FSEND); + + pDefStrm->endElement(XML_cacheSource); + } + + size_t nCount = rCache.GetFieldCount(); + pDefStrm->startElement(XML_cacheFields, + XML_count, OString::number(static_cast<long>(nCount)).getStr(), + FSEND); + + for (size_t i = 0; i < nCount; ++i) + { + OUString aName = rCache.GetDimensionName(i); + + pDefStrm->startElement(XML_cacheField, + XML_name, XclXmlUtils::ToOString(aName).getStr(), + XML_numFmtId, OString::number(0).getStr(), + FSEND); + + const ScDPCache::ItemsType& rFieldItems = rCache.GetDimMemberValues(i); + + pDefStrm->startElement(XML_sharedItems, + XML_count, OString::number(static_cast<long>(rFieldItems.size())).getStr(), + FSEND); + + ScDPCache::ItemsType::const_iterator it = rFieldItems.begin(), itEnd = rFieldItems.end(); + for (; it != itEnd; ++it) + { + const ScDPItemData& rItem = *it; + switch (rItem.GetType()) + { + case ScDPItemData::String: + pDefStrm->singleElement(XML_s, + XML_v, XclXmlUtils::ToOString(rItem.GetString()).getStr(), + FSEND); + break; + case ScDPItemData::Value: + pDefStrm->singleElement(XML_n, + XML_v, OString::number(rItem.GetValue()).getStr(), + FSEND); + break; + case ScDPItemData::Empty: + pDefStrm->singleElement(XML_m, FSEND); + break; + case ScDPItemData::Error: + pDefStrm->singleElement(XML_e, + XML_v, XclXmlUtils::ToOString(rItem.GetString()).getStr(), + FSEND); + break; + case ScDPItemData::GroupValue: + case ScDPItemData::RangeStart: + // TODO : What do we do with these types? + pDefStrm->singleElement(XML_m, FSEND); + break; + default: + ; + } + } + + pDefStrm->endElement(XML_sharedItems); + pDefStrm->endElement(XML_cacheField); + } + + pDefStrm->endElement(XML_cacheFields); + + pDefStrm->endElement(XML_pivotCacheDefinition); +} + +XclExpXmlPivotTableManager::XclExpXmlPivotTableManager( const XclExpRoot& rRoot ) : + XclExpRoot(rRoot), maCaches(rRoot) {} + +void XclExpXmlPivotTableManager::Initialize() +{ + const ScDocument& rDoc = GetDoc(); + if (!rDoc.HasPivotTable()) + // No pivot table to export. + return; + + const ScDPCollection* pDPColl = rDoc.GetDPCollection(); + if (!pDPColl) + return; + + // Go through the caches first. + + std::vector<XclExpXmlPivotCaches::Entry> aCaches; + const ScDPCollection::SheetCaches& rSheetCaches = pDPColl->GetSheetCaches(); + const std::vector<ScRange>& rRanges = rSheetCaches.getAllRanges(); + for (size_t i = 0, n = rRanges.size(); i < n; ++i) + { + const ScDPCache* pCache = rSheetCaches.getExistingCache(rRanges[i]); + if (!pCache) + continue; + + // Get all pivot objects that reference this cache, and set up an + // object to cache ID mapping. + const ScDPCache::ObjectSetType& rRefs = pCache->GetAllReferences(); + ScDPCache::ObjectSetType::const_iterator it = rRefs.begin(), itEnd = rRefs.end(); + for (; it != itEnd; ++it) + maCacheIdMap.insert(CacheIdMapType::value_type(*it, aCaches.size()+1)); + + XclExpXmlPivotCaches::Entry aEntry; + aEntry.meType = XclExpXmlPivotCaches::Worksheet; + aEntry.mpCache = pCache; + aEntry.maSrcRange = rRanges[i]; + aCaches.push_back(aEntry); // Cache ID equals position + 1. + } + + // TODO : Handle name and database caches as well. + + for (size_t i = 0, n = pDPColl->GetCount(); i < n; ++i) + { + const ScDPObject* pDPObj = (*pDPColl)[i]; + assert(pDPObj); // We don't store NULL here. + + // Get the cache ID for this pivot table. + CacheIdMapType::iterator itCache = maCacheIdMap.find(pDPObj); + if (itCache == maCacheIdMap.end()) + // No cache ID found. Something is wrong here.... + continue; + + sal_Int32 nCacheId = itCache->second; + SCTAB nTab = pDPObj->GetOutRange().aStart.Tab(); + + TablesType::iterator it = maTables.find(nTab); + if (it == maTables.end()) + { + // Insert a new instance for this sheet index. + std::pair<TablesType::iterator, bool> r = + maTables.insert(nTab, new XclExpXmlPivotTables(GetRoot(), maCaches)); + it = r.first; + } + + XclExpXmlPivotTables* p = it->second; + p->AppendTable(pDPObj, nCacheId, i+1); + } + + maCaches.SetCaches(aCaches); +} + +XclExpXmlPivotCaches& XclExpXmlPivotTableManager::GetCaches() +{ + return maCaches; +} + +XclExpXmlPivotTables* XclExpXmlPivotTableManager::GetTablesBySheet( SCTAB nTab ) +{ + TablesType::iterator it = maTables.find(nTab); + return it == maTables.end() ? NULL : it->second; +} + +XclExpXmlPivotTables::Entry::Entry( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId ) : + mpTable(pTable), mnCacheId(nCacheId), mnPivotId(nPivotId) {} + +XclExpXmlPivotTables::XclExpXmlPivotTables( const XclExpRoot& rRoot, const XclExpXmlPivotCaches& rCaches ) : + XclExpRoot(rRoot), mrCaches(rCaches) {} + +void XclExpXmlPivotTables::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& pWSStrm = rStrm.GetCurrentStream(); // worksheet stream + + sal_Int32 nCounter = 1; // 1-based + TablesType::iterator it = maTables.begin(), itEnd = maTables.end(); + for (; it != itEnd; ++it, ++nCounter) + { + const ScDPObject& rObj = *it->mpTable; + sal_Int32 nCacheId = it->mnCacheId; + sal_Int32 nPivotId = it->mnPivotId; + + sax_fastparser::FSHelperPtr pPivotStrm = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName("xl/pivotTables/", "pivotTable", nPivotId), + XclXmlUtils::GetStreamName(NULL, "../pivotTables/pivotTable", nPivotId), + pWSStrm->getOutputStream(), + CREATE_XL_CONTENT_TYPE("pivotTable"), + CREATE_OFFICEDOC_RELATION_TYPE("pivotTable"), + NULL); + + rStrm.PushStream(pPivotStrm); + SavePivotTableXml(rStrm, rObj, nCacheId); + rStrm.PopStream(); + } +} + +void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDPObject& rDPObj, sal_Int32 nCacheId ) +{ + typedef boost::unordered_map<OUString, long, OUStringHash> NameToIdMapType; + + const XclExpXmlPivotCaches::Entry* pCacheEntry = mrCaches.GetCache(nCacheId); + if (!pCacheEntry) + // Something is horribly wrong. Check your logic. + return; + + const ScDPCache& rCache = *pCacheEntry->mpCache; + + const ScDPSaveData& rSaveData = *rDPObj.GetSaveData(); + + size_t nFieldCount = rCache.GetFieldCount(); + std::vector<const ScDPSaveDimension*> aCachedDims; + NameToIdMapType aNameToIdMap; + + aCachedDims.reserve(nFieldCount); + for (size_t i = 0; i < nFieldCount; ++i) + { + OUString aName = rCache.GetDimensionName(i); + aNameToIdMap.insert(NameToIdMapType::value_type(aName, aCachedDims.size())); + const ScDPSaveDimension* pDim = rSaveData.GetExistingDimensionByName(aName); + aCachedDims.push_back(pDim); + } + + std::vector<long> aRowFields; + std::vector<long> aColFields; + std::vector<long> aPageFields; + std::vector<long> aDataFields; + + // Use dimensions in the save data to get their correct ordering. + // Dimension order here is significant as they specify the order of + // appearance in each axis. + const ScDPSaveData::DimsType& rDims = rSaveData.GetDimensions(); + + for (size_t i = 0, n = rDims.size(); i < n; ++i) + { + const ScDPSaveDimension& rDim = rDims[i]; + + long nPos = -1; // position in cache + OUString aSrcName = ScDPUtil::getSourceDimensionName(rDim.GetName()); + NameToIdMapType::iterator it = aNameToIdMap.find(aSrcName); + if (it != aNameToIdMap.end()) + nPos = it->second; + + if (nPos == -1) + continue; + + if (!aCachedDims[nPos]) + continue; + + sheet::DataPilotFieldOrientation eOrient = + static_cast<sheet::DataPilotFieldOrientation>(rDim.GetOrientation()); + + switch (eOrient) + { + case sheet::DataPilotFieldOrientation_COLUMN: + aColFields.push_back(nPos); + break; + case sheet::DataPilotFieldOrientation_ROW: + aRowFields.push_back(nPos); + break; + case sheet::DataPilotFieldOrientation_PAGE: + aPageFields.push_back(nPos); + break; + case sheet::DataPilotFieldOrientation_DATA: + aDataFields.push_back(nPos); + break; + case sheet::DataPilotFieldOrientation_HIDDEN: + default: + ; + } + } + + sax_fastparser::FSHelperPtr& pPivotStrm = rStrm.GetCurrentStream(); + pPivotStrm->startElement(XML_pivotTableDefinition, + XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main", + XML_name, XclXmlUtils::ToOString(rDPObj.GetName()).getStr(), + XML_cacheId, OString::number(nCacheId).getStr(), + XML_applyNumberFormats, BS(false), + XML_applyBorderFormats, BS(false), + XML_applyFontFormats, BS(false), + XML_applyPatternFormats, BS(false), + XML_applyAlignmentFormats, BS(false), + XML_applyWidthHeightFormats, BS(false), + XML_dataCaption, "Values", + XML_useAutoFormatting, BS(false), + XML_itemPrintTitles, BS(true), + XML_indent, BS(false), + XML_outline, BS(true), + XML_outlineData, BS(true), + FSEND); + + // NB: Excel's range does not include page field area (if any). + ScRange aOutRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::TABLE); + + sal_Int32 nFirstHeaderRow = aColFields.size(); + sal_Int32 nFirstDataRow = 2; + sal_Int32 nFirstDataCol = 1; + ScRange aResRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::RESULT); + if (aOutRange.IsValid() && aResRange.IsValid()) + { + nFirstDataRow = aResRange.aStart.Row() - aOutRange.aStart.Row(); + nFirstDataCol = aResRange.aStart.Col() - aOutRange.aStart.Col(); + } + + if (!aOutRange.IsValid()) + aOutRange = rDPObj.GetOutRange(); + + pPivotStrm->write("<")->writeId(XML_location); + rStrm.WriteAttributes(XML_ref, + XclXmlUtils::ToOString(aOutRange), + XML_firstHeaderRow, OString::number(nFirstHeaderRow).getStr(), + XML_firstDataRow, OString::number(nFirstDataRow).getStr(), + XML_firstDataCol, OString::number(nFirstDataCol).getStr(), + FSEND); + + if (!aPageFields.empty()) + { + rStrm.WriteAttributes(XML_rowPageCount, OString::number(static_cast<long>(aPageFields.size())).getStr(), FSEND); + rStrm.WriteAttributes(XML_colPageCount, OString::number(1).getStr(), FSEND); + } + + pPivotStrm->write("/>"); + + // <pivotFields> - It must contain all fields in the pivot cache even if + // only some of them are used in the pivot table. The order must be as + // they appear in the cache. + + pPivotStrm->startElement(XML_pivotFields, + XML_count, OString::number(static_cast<long>(aCachedDims.size())).getStr(), + FSEND); + + for (size_t i = 0, n = aCachedDims.size(); i < n; ++i) + { + const ScDPSaveDimension* pDim = aCachedDims[i]; + if (!pDim) + { + pPivotStrm->singleElement(XML_pivotField, + XML_showAll, BS(false), + FSEND); + continue; + } + + sheet::DataPilotFieldOrientation eOrient = + static_cast<sheet::DataPilotFieldOrientation>(pDim->GetOrientation()); + + if (eOrient == sheet::DataPilotFieldOrientation_HIDDEN) + { + pPivotStrm->singleElement(XML_pivotField, + XML_showAll, BS(false), + FSEND); + continue; + } + + if (eOrient == sheet::DataPilotFieldOrientation_DATA) + { + pPivotStrm->singleElement(XML_pivotField, + XML_dataField, BS(true), + XML_showAll, BS(false), + FSEND); + + continue; + } + + pPivotStrm->startElement(XML_pivotField, + XML_axis, toOOXMLAxisType(eOrient), + XML_showAll, BS(false), + FSEND); + + // TODO : Dump field items. + + pPivotStrm->endElement(XML_pivotField); + } + + pPivotStrm->endElement(XML_pivotFields); + + // <rowFields> + + if (!aRowFields.empty()) + { + pPivotStrm->startElement(XML_rowFields, + XML_count, OString::number(static_cast<long>(aRowFields.size())), + FSEND); + + std::vector<long>::iterator it = aRowFields.begin(), itEnd = aRowFields.end(); + for (; it != itEnd; ++it) + { + pPivotStrm->singleElement(XML_field, + XML_x, OString::number(*it), + FSEND); + } + + pPivotStrm->endElement(XML_rowFields); + } + + // <rowItems> + + // <colFields> + + if (!aColFields.empty()) + { + pPivotStrm->startElement(XML_colFields, + XML_count, OString::number(static_cast<long>(aColFields.size())), + FSEND); + + std::vector<long>::iterator it = aColFields.begin(), itEnd = aColFields.end(); + for (; it != itEnd; ++it) + { + pPivotStrm->singleElement(XML_field, + XML_x, OString::number(*it), + FSEND); + } + + pPivotStrm->endElement(XML_colFields); + } + + // <colItems> + + // <pageFields> + + if (!aPageFields.empty()) + { + pPivotStrm->startElement(XML_pageFields, + XML_count, OString::number(static_cast<long>(aPageFields.size())), + FSEND); + + std::vector<long>::iterator it = aPageFields.begin(), itEnd = aPageFields.end(); + for (; it != itEnd; ++it) + { + pPivotStrm->singleElement(XML_pageField, + XML_fld, OString::number(*it), + XML_hier, OString::number(-1), // TODO : handle this correctly. + FSEND); + } + + pPivotStrm->endElement(XML_pageFields); + } + + // <dataFields> + + if (!aDataFields.empty()) + { + pPivotStrm->startElement(XML_dataFields, + XML_count, OString::number(static_cast<long>(aDataFields.size())), + FSEND); + + std::vector<long>::iterator it = aDataFields.begin(), itEnd = aDataFields.end(); + for (; it != itEnd; ++it) + { + long nDimIdx = *it; + assert(aCachedDims[nDimIdx]); // the loop above should have screened for NULL's. + const ScDPSaveDimension& rDim = *aCachedDims[nDimIdx]; + const OUString* pName = rDim.GetLayoutName(); + pPivotStrm->write("<")->writeId(XML_dataField); + if (pName) + rStrm.WriteAttributes(XML_name, XclXmlUtils::ToOString(*pName), FSEND); + + rStrm.WriteAttributes(XML_fld, OString::number(nDimIdx).getStr(), FSEND); + + pPivotStrm->write("/>"); + } + + pPivotStrm->endElement(XML_dataFields); + } + + OUStringBuffer aBuf("../pivotCache/pivotCacheDefinition"); + aBuf.append(nCacheId); + aBuf.append(".xml"); + + rStrm.addRelation( + pPivotStrm->getOutputStream(), + CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"), + aBuf.makeStringAndClear()); + + pPivotStrm->endElement(XML_pivotTableDefinition); +} + +void XclExpXmlPivotTables::AppendTable( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId ) +{ + maTables.push_back(Entry(pTable, nCacheId, nPivotId)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/filter/excel/xeroot.cxx b/sc/source/filter/excel/xeroot.cxx index 1cab39a7b60f..42f03d403944 100644 --- a/sc/source/filter/excel/xeroot.cxx +++ b/sc/source/filter/excel/xeroot.cxx @@ -37,6 +37,7 @@ #include "xepivot.hxx" #include "xestyle.hxx" #include "xeroot.hxx" +#include <xepivotxml.hxx> #include "excrecds.hxx" #include "tabprotection.hxx" @@ -164,6 +165,12 @@ XclExpPivotTableManager& XclExpRoot::GetPivotTableManager() const return *mrExpData.mxPTableMgr; } +XclExpXmlPivotTableManager& XclExpRoot::GetXmlPivotTableManager() +{ + assert(mrExpData.mxXmlPTableMgr); + return *mrExpData.mxXmlPTableMgr; +} + void XclExpRoot::InitializeConvert() { mrExpData.mxTabInfo.reset( new XclExpTabInfo( GetRoot() ) ); @@ -201,6 +208,8 @@ void XclExpRoot::InitializeGlobals() if( GetOutput() == EXC_OUTPUT_XML_2007 ) { + mrExpData.mxXmlPTableMgr.reset(new XclExpXmlPivotTableManager(GetRoot())); + do { ScDocument& rDoc = GetDoc(); diff --git a/sc/source/filter/inc/xepivotxml.hxx b/sc/source/filter/inc/xepivotxml.hxx new file mode 100644 index 000000000000..b6710606b0ab --- /dev/null +++ b/sc/source/filter/inc/xepivotxml.hxx @@ -0,0 +1,95 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SC_FILTER_XEPIVOTXML_HXX +#define INCLUDED_SC_FILTER_XEPIVOTXML_HXX + +#include <xerecord.hxx> +#include <xeroot.hxx> + +#include <boost/ptr_container/ptr_map.hpp> +#include <boost/unordered_map.hpp> + +class ScDPCache; +class ScDPCollection; +class ScDPObject; + +class XclExpXmlPivotCaches : public XclExpRecordBase, protected XclExpRoot +{ +public: + enum EntryType { Worksheet, Name, Database }; + + struct Entry + { + const ScDPCache* mpCache; + EntryType meType; + ScRange maSrcRange; + }; + + XclExpXmlPivotCaches( const XclExpRoot& rRoot ); + virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE; + + void SetCaches( const std::vector<Entry>& rCaches ); + bool HasCaches() const; + const Entry* GetCache( sal_Int32 nCacheId ) const; + +private: + void SavePivotCacheXml( XclExpXmlStream& rStrm, const Entry& rEntry, sal_Int32 nCacheId ); + +private: + std::vector<Entry> maCaches; +}; + +class XclExpXmlPivotTables : public XclExpRecordBase, protected XclExpRoot +{ + struct Entry + { + const ScDPObject* mpTable; + sal_Int32 mnCacheId; + sal_Int32 mnPivotId; /// used as [n] in pivotTable[n].xml part name. + + Entry( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId ); + }; + + const XclExpXmlPivotCaches& mrCaches; + typedef std::vector<Entry> TablesType; + TablesType maTables; + +public: + XclExpXmlPivotTables( const XclExpRoot& rRoot, const XclExpXmlPivotCaches& rCaches ); + + virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE; + + void AppendTable( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId ); + +private: + void SavePivotTableXml( XclExpXmlStream& rStrm, const ScDPObject& rObj, sal_Int32 nCacheId ); +}; + +class XclExpXmlPivotTableManager : protected XclExpRoot +{ + typedef boost::ptr_map<SCTAB, XclExpXmlPivotTables> TablesType; + typedef boost::unordered_map<const ScDPObject*, sal_Int32> CacheIdMapType; +public: + XclExpXmlPivotTableManager( const XclExpRoot& rRoot ); + + void Initialize(); + + XclExpXmlPivotCaches& GetCaches(); + XclExpXmlPivotTables* GetTablesBySheet( SCTAB nTab ); + +private: + XclExpXmlPivotCaches maCaches; + TablesType maTables; + CacheIdMapType maCacheIdMap; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/filter/inc/xeroot.hxx b/sc/source/filter/inc/xeroot.hxx index 4906a187e374..35de5c62c52b 100644 --- a/sc/source/filter/inc/xeroot.hxx +++ b/sc/source/filter/inc/xeroot.hxx @@ -51,6 +51,7 @@ class XclExpObjectManager; class XclExpFilterManager; class XclExpPivotTableManager; class XclExpDxfs; +class XclExpXmlPivotTableManager; /** Stores global buffers and data needed for Excel export filter. */ struct XclExpRootData : public XclRootData @@ -90,6 +91,8 @@ struct XclExpRootData : public XclRootData XclExpPTableMgrRef mxPTableMgr; /// All pivot tables and pivot caches. XclExpDxfsRef mxDxfs; /// All delta formatting entries + boost::shared_ptr<XclExpXmlPivotTableManager> mxXmlPTableMgr; + ScCompiler::OpCodeMapPtr mxOpCodeMap; /// mapping between op-codes and names bool mbRelUrl; /// true = Store URLs relative. @@ -144,6 +147,8 @@ public: /** Returns the differential formatting list */ XclExpDxfs& GetDxfs() const; + XclExpXmlPivotTableManager& GetXmlPivotTableManager(); + /** Is called when export filter starts to create the Excel document (all BIFF versions). */ void InitializeConvert(); /** Is called when export filter starts to create the workbook global data (>=BIFF5). */ |