summaryrefslogtreecommitdiff
path: root/oox/source/xls/pivotcachebuffer.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'oox/source/xls/pivotcachebuffer.cxx')
-rw-r--r--oox/source/xls/pivotcachebuffer.cxx1548
1 files changed, 1548 insertions, 0 deletions
diff --git a/oox/source/xls/pivotcachebuffer.cxx b/oox/source/xls/pivotcachebuffer.cxx
new file mode 100644
index 000000000000..d76ab81d07c4
--- /dev/null
+++ b/oox/source/xls/pivotcachebuffer.cxx
@@ -0,0 +1,1548 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include "oox/xls/pivotcachebuffer.hxx"
+#include <set>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/table/XCell.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupInfo.hpp>
+#include <com/sun/star/sheet/XDataPilotFieldGrouping.hpp>
+#include "properties.hxx"
+#include "oox/helper/attributelist.hxx"
+#include "oox/helper/propertyset.hxx"
+#include "oox/helper/recordinputstream.hxx"
+#include "oox/core/filterbase.hxx"
+#include "oox/xls/biffinputstream.hxx"
+#include "oox/xls/defnamesbuffer.hxx"
+#include "oox/xls/excelhandlers.hxx"
+#include "oox/xls/pivotcachefragment.hxx"
+#include "oox/xls/tablebuffer.hxx"
+#include "oox/xls/unitconverter.hxx"
+#include "oox/xls/worksheetbuffer.hxx"
+
+using ::rtl::OUString;
+using ::rtl::OUStringBuffer;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::container::XIndexAccess;
+using ::com::sun::star::container::XNameAccess;
+using ::com::sun::star::container::XNamed;
+using ::com::sun::star::util::DateTime;
+using ::com::sun::star::table::CellAddress;
+using ::com::sun::star::sheet::DataPilotFieldGroupInfo;
+using ::com::sun::star::table::XCell;
+using ::com::sun::star::sheet::XDataPilotField;
+using ::com::sun::star::sheet::XDataPilotFieldGrouping;
+using ::oox::core::Relations;
+
+namespace oox {
+namespace xls {
+
+// ============================================================================
+
+namespace {
+
+const sal_uInt16 OOBIN_PCDFIELD_SERVERFIELD = 0x0001;
+const sal_uInt16 OOBIN_PCDFIELD_NOUNIQUEITEMS = 0x0002;
+const sal_uInt16 OOBIN_PCDFIELD_DATABASEFIELD = 0x0004;
+const sal_uInt16 OOBIN_PCDFIELD_HASCAPTION = 0x0008;
+const sal_uInt16 OOBIN_PCDFIELD_MEMBERPROPFIELD = 0x0010;
+const sal_uInt16 OOBIN_PCDFIELD_HASFORMULA = 0x0100;
+const sal_uInt16 OOBIN_PCDFIELD_HASPROPERTYNAME = 0x0200;
+
+const sal_uInt16 OOBIN_PCDFSITEMS_HASSEMIMIXED = 0x0001;
+const sal_uInt16 OOBIN_PCDFSITEMS_HASNONDATE = 0x0002;
+const sal_uInt16 OOBIN_PCDFSITEMS_HASDATE = 0x0004;
+const sal_uInt16 OOBIN_PCDFSITEMS_HASSTRING = 0x0008;
+const sal_uInt16 OOBIN_PCDFSITEMS_HASBLANK = 0x0010;
+const sal_uInt16 OOBIN_PCDFSITEMS_HASMIXED = 0x0020;
+const sal_uInt16 OOBIN_PCDFSITEMS_ISNUMERIC = 0x0040;
+const sal_uInt16 OOBIN_PCDFSITEMS_ISINTEGER = 0x0080;
+const sal_uInt16 OOBIN_PCDFSITEMS_HASMINMAX = 0x0100;
+const sal_uInt16 OOBIN_PCDFSITEMS_HASLONGTEXT = 0x0200;
+
+const sal_uInt16 OOBIN_PCITEM_ARRAY_DOUBLE = 0x0001;
+const sal_uInt16 OOBIN_PCITEM_ARRAY_STRING = 0x0002;
+const sal_uInt16 OOBIN_PCITEM_ARRAY_ERROR = 0x0010;
+const sal_uInt16 OOBIN_PCITEM_ARRAY_DATE = 0x0020;
+
+const sal_uInt8 OOBIN_PCDFRANGEPR_AUTOSTART = 0x01;
+const sal_uInt8 OOBIN_PCDFRANGEPR_AUTOEND = 0x02;
+const sal_uInt8 OOBIN_PCDFRANGEPR_DATEGROUP = 0x04;
+
+const sal_uInt8 OOBIN_PCDEFINITION_SAVEDATA = 0x01;
+const sal_uInt8 OOBIN_PCDEFINITION_INVALID = 0x02;
+const sal_uInt8 OOBIN_PCDEFINITION_REFRESHONLOAD = 0x04;
+const sal_uInt8 OOBIN_PCDEFINITION_OPTIMIZEMEMORY = 0x08;
+const sal_uInt8 OOBIN_PCDEFINITION_ENABLEREFRESH = 0x10;
+const sal_uInt8 OOBIN_PCDEFINITION_BACKGROUNDQUERY = 0x20;
+const sal_uInt8 OOBIN_PCDEFINITION_UPGRADEONREFR = 0x40;
+const sal_uInt8 OOBIN_PCDEFINITION_TUPELCACHE = 0x80;
+
+const sal_uInt8 OOBIN_PCDEFINITION_HASUSERNAME = 0x01;
+const sal_uInt8 OOBIN_PCDEFINITION_HASRELID = 0x02;
+const sal_uInt8 OOBIN_PCDEFINITION_SUPPORTSUBQUERY = 0x04;
+const sal_uInt8 OOBIN_PCDEFINITION_SUPPORTDRILL = 0x08;
+
+const sal_uInt8 OOBIN_PCDWBSOURCE_HASRELID = 0x01;
+const sal_uInt8 OOBIN_PCDWBSOURCE_HASSHEET = 0x02;
+
+const sal_uInt16 BIFF_PCDSOURCE_WORKSHEET = 0x0001;
+const sal_uInt16 BIFF_PCDSOURCE_EXTERNAL = 0x0002;
+const sal_uInt16 BIFF_PCDSOURCE_CONSOLIDATION = 0x0004;
+const sal_uInt16 BIFF_PCDSOURCE_SCENARIO = 0x0010;
+
+const sal_uInt16 BIFF_PC_NOSTRING = 0xFFFF;
+
+const sal_uInt16 BIFF_PCDFIELD_HASITEMS = 0x0001;
+const sal_uInt16 BIFF_PCDFIELD_HASUNSHAREDITEMS = 0x0002;
+const sal_uInt16 BIFF_PCDFIELD_CALCULATED = 0x0004;
+const sal_uInt16 BIFF_PCDFIELD_HASPARENT = 0x0008;
+const sal_uInt16 BIFF_PCDFIELD_RANGEGROUP = 0x0010;
+const sal_uInt16 BIFF_PCDFIELD_ISNUMERIC = 0x0020;
+const sal_uInt16 BIFF_PCDFIELD_HASSEMIMIXED = 0x0080;
+const sal_uInt16 BIFF_PCDFIELD_HASMINMAX = 0x0100;
+const sal_uInt16 BIFF_PCDFIELD_HASLONGINDEX = 0x0200;
+const sal_uInt16 BIFF_PCDFIELD_HASNONDATE = 0x0400;
+const sal_uInt16 BIFF_PCDFIELD_HASDATE = 0x0800;
+const sal_uInt16 BIFF_PCDFIELD_SERVERFIELD = 0x2000;
+const sal_uInt16 BIFF_PCDFIELD_NOUNIQUEITEMS = 0x4000;
+
+const sal_uInt16 BIFF_PCDFRANGEPR_AUTOSTART = 0x0001;
+const sal_uInt16 BIFF_PCDFRANGEPR_AUTOEND = 0x0002;
+
+const sal_uInt16 BIFF_PCDEFINITION_SAVEDATA = 0x0001;
+const sal_uInt16 BIFF_PCDEFINITION_INVALID = 0x0002;
+const sal_uInt16 BIFF_PCDEFINITION_REFRESHONLOAD = 0x0004;
+const sal_uInt16 BIFF_PCDEFINITION_OPTIMIZEMEMORY = 0x0008;
+const sal_uInt16 BIFF_PCDEFINITION_BACKGROUNDQUERY = 0x0010;
+const sal_uInt16 BIFF_PCDEFINITION_ENABLEREFRESH = 0x0020;
+
+// ----------------------------------------------------------------------------
+
+/** Adjusts the weird date format read from binary streams.
+
+ Dates before 1900-Mar-01 are stored including the non-existing leap day
+ 1900-02-29. Time values (without date) are stored as times of day
+ 1900-Jan-00. Nothing has to be done when the workbook is stored in 1904
+ date mode (dates before 1904-Jan-01 will not occur in this case).
+ */
+void lclAdjustBinDateTime( DateTime& orDateTime )
+{
+ if( (orDateTime.Year == 1900) && (orDateTime.Month <= 2) )
+ {
+ OSL_ENSURE( (orDateTime.Month == 1) || ((orDateTime.Month == 2) && (orDateTime.Day > 0)), "lclAdjustBinDateTime - invalid date" );
+ switch( orDateTime.Month )
+ {
+ case 2: if( orDateTime.Day > 1 ) --orDateTime.Day; else { orDateTime.Day += 30; --orDateTime.Month; } break;
+ case 1: if( orDateTime.Day > 1 ) --orDateTime.Day; else { orDateTime.Day += 30; orDateTime.Month = 12; --orDateTime.Year; } break;
+ }
+ }
+}
+
+} // namespace
+
+// ============================================================================
+
+PivotCacheItem::PivotCacheItem() :
+ mnType( XML_m )
+{
+}
+
+void PivotCacheItem::readString( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getXString( XML_v, OUString() );
+ mnType = XML_s;
+}
+
+void PivotCacheItem::readNumeric( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getDouble( XML_v, 0.0 );
+ mnType = XML_n;
+}
+
+void PivotCacheItem::readDate( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getDateTime( XML_v, DateTime() );
+ mnType = XML_d;
+}
+
+void PivotCacheItem::readBool( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getBool( XML_v, false );
+ mnType = XML_b;
+}
+
+void PivotCacheItem::readError( const AttributeList& rAttribs, const UnitConverter& rUnitConverter )
+{
+ maValue <<= static_cast< sal_Int32 >( rUnitConverter.calcBiffErrorCode( rAttribs.getXString( XML_v, OUString() ) ) );
+ mnType = XML_e;
+}
+
+void PivotCacheItem::readIndex( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getInteger( XML_v, -1 );
+ mnType = XML_x;
+}
+
+void PivotCacheItem::readString( RecordInputStream& rStrm )
+{
+ maValue <<= rStrm.readString();
+ mnType = XML_s;
+}
+
+void PivotCacheItem::readDouble( RecordInputStream& rStrm )
+{
+ maValue <<= rStrm.readDouble();
+ mnType = XML_n;
+}
+
+void PivotCacheItem::readDate( RecordInputStream& rStrm )
+{
+ DateTime aDateTime;
+ aDateTime.Year = rStrm.readuInt16();
+ aDateTime.Month = rStrm.readuInt16();
+ aDateTime.Day = rStrm.readuInt8();
+ aDateTime.Hours = rStrm.readuInt8();
+ aDateTime.Minutes = rStrm.readuInt8();
+ aDateTime.Seconds = rStrm.readuInt8();
+ lclAdjustBinDateTime( aDateTime );
+ maValue <<= aDateTime;
+ mnType = XML_d;
+}
+
+void PivotCacheItem::readBool( RecordInputStream& rStrm )
+{
+ maValue <<= (rStrm.readuInt8() != 0);
+ mnType = XML_b;
+}
+
+void PivotCacheItem::readError( RecordInputStream& rStrm )
+{
+ maValue <<= static_cast< sal_Int32 >( rStrm.readuInt8() );
+ mnType = XML_e;
+}
+
+void PivotCacheItem::readIndex( RecordInputStream& rStrm )
+{
+ maValue <<= rStrm.readInt32();
+ mnType = XML_x;
+}
+
+void PivotCacheItem::readString( BiffInputStream& rStrm, const WorkbookHelper& rHelper )
+{
+ maValue <<= (rHelper.getBiff() == BIFF8) ? rStrm.readUniString() : rStrm.readByteStringUC( true, rHelper.getTextEncoding() );
+ mnType = XML_s;
+}
+
+void PivotCacheItem::readDouble( BiffInputStream& rStrm )
+{
+ maValue <<= rStrm.readDouble();
+ mnType = XML_n;
+}
+
+void PivotCacheItem::readInteger( BiffInputStream& rStrm )
+{
+ maValue <<= rStrm.readInt16();
+ mnType = XML_i; // fake, used for BIFF only
+}
+
+void PivotCacheItem::readDate( BiffInputStream& rStrm )
+{
+ DateTime aDateTime;
+ aDateTime.Year = rStrm.readuInt16();
+ aDateTime.Month = rStrm.readuInt16();
+ aDateTime.Day = rStrm.readuInt8();
+ aDateTime.Hours = rStrm.readuInt8();
+ aDateTime.Minutes = rStrm.readuInt8();
+ aDateTime.Seconds = rStrm.readuInt8();
+ lclAdjustBinDateTime( aDateTime );
+ maValue <<= aDateTime;
+ mnType = XML_d;
+}
+
+void PivotCacheItem::readBool( BiffInputStream& rStrm )
+{
+ maValue <<= (rStrm.readuInt8() != 0);
+ mnType = XML_b;
+}
+
+void PivotCacheItem::readError( BiffInputStream& rStrm )
+{
+ maValue <<= static_cast< sal_Int32 >( rStrm.readuInt8() );
+ mnType = XML_e;
+}
+
+OUString PivotCacheItem::getName() const
+{
+ switch( mnType )
+ {
+ case XML_m: return OUString();
+ case XML_s: return maValue.get< OUString >();
+ case XML_n: return OUString::valueOf( maValue.get< double >() ); // !TODO
+ case XML_i: return OUString::valueOf( maValue.get< sal_Int32 >() );
+ case XML_d: return OUString(); // !TODO
+ case XML_b: return OUString::valueOf( static_cast< sal_Bool >( maValue.get< bool >() ) ); // !TODO
+ case XML_e: return OUString(); // !TODO
+ }
+ OSL_ENSURE( false, "PivotCacheItem::getName - invalid data type" );
+ return OUString();
+}
+
+// ----------------------------------------------------------------------------
+
+PivotCacheItemList::PivotCacheItemList( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void PivotCacheItemList::importItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ PivotCacheItem& rItem = createItem();
+ switch( nElement )
+ {
+ case XLS_TOKEN( m ): break;
+ case XLS_TOKEN( s ): rItem.readString( rAttribs ); break;
+ case XLS_TOKEN( n ): rItem.readNumeric( rAttribs ); break;
+ case XLS_TOKEN( d ): rItem.readDate( rAttribs ); break;
+ case XLS_TOKEN( b ): rItem.readBool( rAttribs ); break;
+ case XLS_TOKEN( e ): rItem.readError( rAttribs, getUnitConverter() ); break;
+ default: OSL_ENSURE( false, "PivotCacheItemList::importItem - unknown element type" );
+ }
+}
+
+void PivotCacheItemList::importItem( sal_Int32 nRecId, RecordInputStream& rStrm )
+{
+ if( nRecId == OOBIN_ID_PCITEM_ARRAY )
+ {
+ importArray( rStrm );
+ return;
+ }
+
+ PivotCacheItem& rItem = createItem();
+ switch( nRecId )
+ {
+ case OOBIN_ID_PCITEM_MISSING:
+ case OOBIN_ID_PCITEMA_MISSING: break;
+ case OOBIN_ID_PCITEM_STRING:
+ case OOBIN_ID_PCITEMA_STRING: rItem.readString( rStrm ); break;
+ case OOBIN_ID_PCITEM_DOUBLE:
+ case OOBIN_ID_PCITEMA_DOUBLE: rItem.readDouble( rStrm ); break;
+ case OOBIN_ID_PCITEM_DATE:
+ case OOBIN_ID_PCITEMA_DATE: rItem.readDate( rStrm ); break;
+ case OOBIN_ID_PCITEM_BOOL:
+ case OOBIN_ID_PCITEMA_BOOL: rItem.readBool( rStrm ); break;
+ case OOBIN_ID_PCITEM_ERROR:
+ case OOBIN_ID_PCITEMA_ERROR: rItem.readError( rStrm ); break;
+ default: OSL_ENSURE( false, "PivotCacheItemList::importItem - unknown record type" );
+ }
+}
+
+void PivotCacheItemList::importItemList( BiffInputStream& rStrm, sal_uInt16 nCount )
+{
+ bool bLoop = true;
+ for( sal_uInt16 nItemIdx = 0; bLoop && (nItemIdx < nCount); ++nItemIdx )
+ {
+ bLoop = rStrm.startNextRecord();
+ if( bLoop ) switch( rStrm.getRecId() )
+ {
+ case BIFF_ID_PCITEM_MISSING: createItem(); break;
+ case BIFF_ID_PCITEM_STRING: createItem().readString( rStrm, *this ); break;
+ case BIFF_ID_PCITEM_DOUBLE: createItem().readDouble( rStrm ); break;
+ case BIFF_ID_PCITEM_INTEGER: createItem().readInteger( rStrm ); break;
+ case BIFF_ID_PCITEM_DATE: createItem().readDate( rStrm ); break;
+ case BIFF_ID_PCITEM_BOOL: createItem().readBool( rStrm ); break;
+ case BIFF_ID_PCITEM_ERROR: createItem().readError( rStrm ); break;
+ default: rStrm.rewindRecord(); bLoop = false;
+ }
+ }
+ OSL_ENSURE( bLoop, "PivotCacheItemList::importItemList - could not read all cache item records" );
+}
+
+const PivotCacheItem* PivotCacheItemList::getCacheItem( sal_Int32 nItemIdx ) const
+{
+ return ContainerHelper::getVectorElement( maItems, nItemIdx );
+}
+
+void PivotCacheItemList::getCacheItemNames( ::std::vector< OUString >& orItemNames ) const
+{
+ orItemNames.clear();
+ orItemNames.reserve( maItems.size() );
+ for( CacheItemVector::const_iterator aIt = maItems.begin(), aEnd = maItems.end(); aIt != aEnd; ++aIt )
+ orItemNames.push_back( aIt->getName() );
+}
+
+// private --------------------------------------------------------------------
+
+PivotCacheItem& PivotCacheItemList::createItem()
+{
+ maItems.resize( maItems.size() + 1 );
+ return maItems.back();
+}
+
+void PivotCacheItemList::importArray( RecordInputStream& rStrm )
+{
+ sal_uInt16 nType = rStrm.readuInt16();
+ sal_Int32 nCount = rStrm.readInt32();
+ for( sal_Int32 nIdx = 0; !rStrm.isEof() && (nIdx < nCount); ++nIdx )
+ {
+ switch( nType )
+ {
+ case OOBIN_PCITEM_ARRAY_DOUBLE: createItem().readDouble( rStrm ); break;
+ case OOBIN_PCITEM_ARRAY_STRING: createItem().readString( rStrm ); break;
+ case OOBIN_PCITEM_ARRAY_ERROR: createItem().readError( rStrm ); break;
+ case OOBIN_PCITEM_ARRAY_DATE: createItem().readDate( rStrm ); break;
+ default:
+ OSL_ENSURE( false, "PivotCacheItemList::importArray - unknown data type" );
+ nIdx = nCount;
+ }
+ }
+}
+
+// ============================================================================
+
+PCFieldModel::PCFieldModel() :
+ mnNumFmtId( 0 ),
+ mnSqlType( 0 ),
+ mnHierarchy( 0 ),
+ mnLevel( 0 ),
+ mnMappingCount( 0 ),
+ mbDatabaseField( true ),
+ mbServerField( false ),
+ mbUniqueList( true ),
+ mbMemberPropField( false )
+{
+}
+
+// ----------------------------------------------------------------------------
+
+PCSharedItemsModel::PCSharedItemsModel() :
+ mbHasSemiMixed( true ),
+ mbHasNonDate( true ),
+ mbHasDate( false ),
+ mbHasString( true ),
+ mbHasBlank( false ),
+ mbHasMixed( false ),
+ mbIsNumeric( false ),
+ mbIsInteger( false ),
+ mbHasLongText( false ),
+ mbHasLongIndexes( false )
+{
+}
+
+// ----------------------------------------------------------------------------
+
+PCFieldGroupModel::PCFieldGroupModel() :
+ mfStartValue( 0.0 ),
+ mfEndValue( 0.0 ),
+ mfInterval( 1.0 ),
+ mnParentField( -1 ),
+ mnBaseField( -1 ),
+ mnGroupBy( XML_range ),
+ mbRangeGroup( false ),
+ mbDateGroup( false ),
+ mbAutoStart( true ),
+ mbAutoEnd( true )
+{
+}
+
+void PCFieldGroupModel::setBinGroupBy( sal_uInt8 nGroupBy )
+{
+ static const sal_Int32 spnGroupBy[] = { XML_range,
+ XML_seconds, XML_minutes, XML_hours, XML_days, XML_months, XML_quarters, XML_years };
+ mnGroupBy = STATIC_ARRAY_SELECT( spnGroupBy, nGroupBy, XML_range );
+}
+
+// ----------------------------------------------------------------------------
+
+PivotCacheField::PivotCacheField( const WorkbookHelper& rHelper, bool bIsDatabaseField ) :
+ WorkbookHelper( rHelper ),
+ maSharedItems( rHelper ),
+ maGroupItems( rHelper )
+{
+ maFieldModel.mbDatabaseField = bIsDatabaseField;
+}
+
+void PivotCacheField::importCacheField( const AttributeList& rAttribs )
+{
+ maFieldModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maFieldModel.maCaption = rAttribs.getXString( XML_caption, OUString() );
+ maFieldModel.maPropertyName = rAttribs.getXString( XML_propertyName, OUString() );
+ maFieldModel.maFormula = rAttribs.getXString( XML_formula, OUString() );
+ maFieldModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 );
+ maFieldModel.mnSqlType = rAttribs.getInteger( XML_sqlType, 0 );
+ maFieldModel.mnHierarchy = rAttribs.getInteger( XML_hierarchy, 0 );
+ maFieldModel.mnLevel = rAttribs.getInteger( XML_level, 0 );
+ maFieldModel.mnMappingCount = rAttribs.getInteger( XML_mappingCount, 0 );
+ maFieldModel.mbDatabaseField = rAttribs.getBool( XML_databaseField, true );
+ maFieldModel.mbServerField = rAttribs.getBool( XML_serverField, false );
+ maFieldModel.mbUniqueList = rAttribs.getBool( XML_uniqueList, true );
+ maFieldModel.mbMemberPropField = rAttribs.getBool( XML_memberPropertyField, false );
+}
+
+void PivotCacheField::importSharedItems( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( maSharedItems.empty(), "PivotCacheField::importSharedItems - multiple shared items elements" );
+ maSharedItemsModel.mbHasSemiMixed = rAttribs.getBool( XML_containsSemiMixedTypes, true );
+ maSharedItemsModel.mbHasNonDate = rAttribs.getBool( XML_containsNonDate, true );
+ maSharedItemsModel.mbHasDate = rAttribs.getBool( XML_containsDate, false );
+ maSharedItemsModel.mbHasString = rAttribs.getBool( XML_containsString, true );
+ maSharedItemsModel.mbHasBlank = rAttribs.getBool( XML_containsBlank, false );
+ maSharedItemsModel.mbHasMixed = rAttribs.getBool( XML_containsMixedTypes, false );
+ maSharedItemsModel.mbIsNumeric = rAttribs.getBool( XML_containsNumber, false );
+ maSharedItemsModel.mbIsInteger = rAttribs.getBool( XML_containsInteger, false );
+ maSharedItemsModel.mbHasLongText = rAttribs.getBool( XML_longText, false );
+}
+
+void PivotCacheField::importSharedItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ maSharedItems.importItem( nElement, rAttribs );
+}
+
+void PivotCacheField::importFieldGroup( const AttributeList& rAttribs )
+{
+ maFieldGroupModel.mnParentField = rAttribs.getInteger( XML_par, -1 );
+ maFieldGroupModel.mnBaseField = rAttribs.getInteger( XML_base, -1 );
+}
+
+void PivotCacheField::importRangePr( const AttributeList& rAttribs )
+{
+ maFieldGroupModel.maStartDate = rAttribs.getDateTime( XML_startDate, DateTime() );
+ maFieldGroupModel.maEndDate = rAttribs.getDateTime( XML_endDate, DateTime() );
+ maFieldGroupModel.mfStartValue = rAttribs.getDouble( XML_startNum, 0.0 );
+ maFieldGroupModel.mfEndValue = rAttribs.getDouble( XML_endNum, 0.0 );
+ maFieldGroupModel.mfInterval = rAttribs.getDouble( XML_groupInterval, 1.0 );
+ maFieldGroupModel.mnGroupBy = rAttribs.getToken( XML_groupBy, XML_range );
+ maFieldGroupModel.mbRangeGroup = true;
+ maFieldGroupModel.mbDateGroup = maFieldGroupModel.mnGroupBy != XML_range;
+ maFieldGroupModel.mbAutoStart = rAttribs.getBool( XML_autoStart, true );
+ maFieldGroupModel.mbAutoEnd = rAttribs.getBool( XML_autoEnd, true );
+}
+
+void PivotCacheField::importDiscretePrItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ OSL_ENSURE( nElement == XLS_TOKEN( x ), "PivotCacheField::importDiscretePrItem - unexpected element" );
+ if( nElement == XLS_TOKEN( x ) )
+ maDiscreteItems.push_back( rAttribs.getInteger( XML_v, -1 ) );
+}
+
+void PivotCacheField::importGroupItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ maGroupItems.importItem( nElement, rAttribs );
+}
+
+void PivotCacheField::importPCDField( RecordInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ rStrm >> nFlags >> maFieldModel.mnNumFmtId;
+ maFieldModel.mnSqlType = rStrm.readInt16();
+ rStrm >> maFieldModel.mnHierarchy >> maFieldModel.mnLevel >> maFieldModel.mnMappingCount >> maFieldModel.maName;
+ if( getFlag( nFlags, OOBIN_PCDFIELD_HASCAPTION ) )
+ rStrm >> maFieldModel.maCaption;
+ if( getFlag( nFlags, OOBIN_PCDFIELD_HASFORMULA ) )
+ rStrm.skip( ::std::max< sal_Int32 >( rStrm.readInt32(), 0 ) );
+ if( maFieldModel.mnMappingCount > 0 )
+ rStrm.skip( ::std::max< sal_Int32 >( rStrm.readInt32(), 0 ) );
+ if( getFlag( nFlags, OOBIN_PCDFIELD_HASPROPERTYNAME ) )
+ rStrm >> maFieldModel.maPropertyName;
+
+ maFieldModel.mbDatabaseField = getFlag( nFlags, OOBIN_PCDFIELD_DATABASEFIELD );
+ maFieldModel.mbServerField = getFlag( nFlags, OOBIN_PCDFIELD_SERVERFIELD );
+ maFieldModel.mbUniqueList = !getFlag( nFlags, OOBIN_PCDFIELD_NOUNIQUEITEMS );
+ maFieldModel.mbMemberPropField = getFlag( nFlags, OOBIN_PCDFIELD_MEMBERPROPFIELD );
+}
+
+void PivotCacheField::importPCDFSharedItems( RecordInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ rStrm >> nFlags;
+ maSharedItemsModel.mbHasSemiMixed = getFlag( nFlags, OOBIN_PCDFSITEMS_HASSEMIMIXED );
+ maSharedItemsModel.mbHasNonDate = getFlag( nFlags, OOBIN_PCDFSITEMS_HASNONDATE );
+ maSharedItemsModel.mbHasDate = getFlag( nFlags, OOBIN_PCDFSITEMS_HASDATE );
+ maSharedItemsModel.mbHasString = getFlag( nFlags, OOBIN_PCDFSITEMS_HASSTRING );
+ maSharedItemsModel.mbHasBlank = getFlag( nFlags, OOBIN_PCDFSITEMS_HASBLANK );
+ maSharedItemsModel.mbHasMixed = getFlag( nFlags, OOBIN_PCDFSITEMS_HASMIXED );
+ maSharedItemsModel.mbIsNumeric = getFlag( nFlags, OOBIN_PCDFSITEMS_ISNUMERIC );
+ maSharedItemsModel.mbIsInteger = getFlag( nFlags, OOBIN_PCDFSITEMS_ISINTEGER );
+ maSharedItemsModel.mbHasLongText = getFlag( nFlags, OOBIN_PCDFSITEMS_HASLONGTEXT );
+}
+
+void PivotCacheField::importPCDFSharedItem( sal_Int32 nRecId, RecordInputStream& rStrm )
+{
+ maSharedItems.importItem( nRecId, rStrm );
+}
+
+void PivotCacheField::importPCDFieldGroup( RecordInputStream& rStrm )
+{
+ rStrm >> maFieldGroupModel.mnParentField >> maFieldGroupModel.mnBaseField;
+}
+
+void PivotCacheField::importPCDFRangePr( RecordInputStream& rStrm )
+{
+ sal_uInt8 nGroupBy, nFlags;
+ rStrm >> nGroupBy >> nFlags >> maFieldGroupModel.mfStartValue >> maFieldGroupModel.mfEndValue >> maFieldGroupModel.mfInterval;
+
+ maFieldGroupModel.setBinGroupBy( nGroupBy );
+ maFieldGroupModel.mbRangeGroup = true;
+ maFieldGroupModel.mbDateGroup = getFlag( nFlags, OOBIN_PCDFRANGEPR_DATEGROUP );
+ maFieldGroupModel.mbAutoStart = getFlag( nFlags, OOBIN_PCDFRANGEPR_AUTOSTART );
+ maFieldGroupModel.mbAutoEnd = getFlag( nFlags, OOBIN_PCDFRANGEPR_AUTOEND );
+
+ OSL_ENSURE( maFieldGroupModel.mbDateGroup == (maFieldGroupModel.mnGroupBy != XML_range), "PivotCacheField::importPCDFRangePr - wrong date flag" );
+ if( maFieldGroupModel.mbDateGroup )
+ {
+ maFieldGroupModel.maStartDate = getUnitConverter().calcDateTimeFromSerial( maFieldGroupModel.mfStartValue );
+ maFieldGroupModel.maEndDate = getUnitConverter().calcDateTimeFromSerial( maFieldGroupModel.mfEndValue );
+ }
+}
+
+void PivotCacheField::importPCDFDiscretePrItem( sal_Int32 nRecId, RecordInputStream& rStrm )
+{
+ OSL_ENSURE( nRecId == OOBIN_ID_PCITEM_INDEX, "PivotCacheField::importPCDFDiscretePrItem - unexpected record" );
+ if( nRecId == OOBIN_ID_PCITEM_INDEX )
+ maDiscreteItems.push_back( rStrm.readInt32() );
+}
+
+void PivotCacheField::importPCDFGroupItem( sal_Int32 nRecId, RecordInputStream& rStrm )
+{
+ maGroupItems.importItem( nRecId, rStrm );
+}
+
+void PivotCacheField::importPCDField( BiffInputStream& rStrm )
+{
+ sal_uInt16 nFlags, nGroupItems, nBaseItems, nSharedItems;
+ rStrm >> nFlags;
+ maFieldGroupModel.mnParentField = rStrm.readuInt16();
+ maFieldGroupModel.mnBaseField = rStrm.readuInt16();
+ rStrm.skip( 2 ); // number of unique items (either shared or group)
+ rStrm >> nGroupItems >> nBaseItems >> nSharedItems;
+ maFieldModel.maName = (getBiff() == BIFF8) ? rStrm.readUniString() : rStrm.readByteStringUC( true, getTextEncoding() );
+
+ maFieldModel.mbServerField = getFlag( nFlags, BIFF_PCDFIELD_SERVERFIELD );
+ maFieldModel.mbUniqueList = !getFlag( nFlags, BIFF_PCDFIELD_NOUNIQUEITEMS );
+ maSharedItemsModel.mbHasSemiMixed = getFlag( nFlags, BIFF_PCDFIELD_HASSEMIMIXED );
+ maSharedItemsModel.mbHasNonDate = getFlag( nFlags, BIFF_PCDFIELD_HASNONDATE );
+ maSharedItemsModel.mbHasDate = getFlag( nFlags, BIFF_PCDFIELD_HASDATE );
+ maSharedItemsModel.mbIsNumeric = getFlag( nFlags, BIFF_PCDFIELD_ISNUMERIC );
+ maSharedItemsModel.mbHasLongIndexes = getFlag( nFlags, BIFF_PCDFIELD_HASLONGINDEX );
+ maFieldGroupModel.mbRangeGroup = getFlag( nFlags, BIFF_PCDFIELD_RANGEGROUP );
+
+ // in BIFF, presence of parent group field is denoted by a flag
+ if( !getFlag( nFlags, BIFF_PCDFIELD_HASPARENT ) )
+ maFieldGroupModel.mnParentField = -1;
+
+ // following PCDFSQLTYPE record contains SQL type
+ if( (rStrm.getNextRecId() == BIFF_ID_PCDFSQLTYPE) && rStrm.startNextRecord() )
+ maFieldModel.mnSqlType = rStrm.readInt16();
+
+ // read group items, if any
+ if( nGroupItems > 0 )
+ {
+ OSL_ENSURE( getFlag( nFlags, BIFF_PCDFIELD_HASITEMS ), "PivotCacheField::importPCDField - missing items flag" );
+ maGroupItems.importItemList( rStrm, nGroupItems );
+
+ sal_uInt16 nNextRecId = rStrm.getNextRecId();
+ bool bHasRangePr = nNextRecId == BIFF_ID_PCDFRANGEPR;
+ bool bHasDiscretePr = nNextRecId == BIFF_ID_PCDFDISCRETEPR;
+
+ OSL_ENSURE( bHasRangePr || bHasDiscretePr, "PivotCacheField::importPCDField - missing group properties record" );
+ OSL_ENSURE( bHasRangePr == maFieldGroupModel.mbRangeGroup, "PivotCacheField::importPCDField - invalid range grouping flag" );
+ if( bHasRangePr && rStrm.startNextRecord() )
+ importPCDFRangePr( rStrm );
+ else if( bHasDiscretePr && rStrm.startNextRecord() )
+ importPCDFDiscretePr( rStrm );
+ }
+
+ // read the shared items, if any
+ if( nSharedItems > 0 )
+ {
+ OSL_ENSURE( getFlag( nFlags, BIFF_PCDFIELD_HASITEMS ), "PivotCacheField::importPCDField - missing items flag" );
+ maSharedItems.importItemList( rStrm, nSharedItems );
+ }
+}
+
+void PivotCacheField::importPCDFRangePr( BiffInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ rStrm >> nFlags;
+ maFieldGroupModel.setBinGroupBy( extractValue< sal_uInt8 >( nFlags, 2, 3 ) );
+ maFieldGroupModel.mbRangeGroup = true;
+ maFieldGroupModel.mbDateGroup = maFieldGroupModel.mnGroupBy != XML_range;
+ maFieldGroupModel.mbAutoStart = getFlag( nFlags, BIFF_PCDFRANGEPR_AUTOSTART );
+ maFieldGroupModel.mbAutoEnd = getFlag( nFlags, BIFF_PCDFRANGEPR_AUTOEND );
+
+ /* Start, end, and interval are stored in 3 separate item records. Type of
+ the items is dependent on numeric/date mode. Numeric groups expect
+ three PCITEM_DOUBLE records, date groups expect two PCITEM_DATE records
+ and one PCITEM_INT record. */
+ PivotCacheItemList aLimits( *this );
+ aLimits.importItemList( rStrm, 3 );
+ OSL_ENSURE( aLimits.size() == 3, "PivotCacheField::importPCDFRangePr - missing grouping records" );
+ const PivotCacheItem* pStartValue = aLimits.getCacheItem( 0 );
+ const PivotCacheItem* pEndValue = aLimits.getCacheItem( 1 );
+ const PivotCacheItem* pInterval = aLimits.getCacheItem( 2 );
+ if( pStartValue && pEndValue && pInterval )
+ {
+ if( maFieldGroupModel.mbDateGroup )
+ {
+ bool bHasTypes = (pStartValue->getType() == XML_d) && (pEndValue->getType() == XML_d) && (pInterval->getType() == XML_i);
+ OSL_ENSURE( bHasTypes, "PivotCacheField::importPCDFRangePr - wrong data types in grouping items" );
+ if( bHasTypes )
+ {
+ maFieldGroupModel.maStartDate = pStartValue->getValue().get< DateTime >();
+ maFieldGroupModel.maEndDate = pEndValue->getValue().get< DateTime >();
+ maFieldGroupModel.mfInterval = pInterval->getValue().get< sal_Int16 >();
+ }
+ }
+ else
+ {
+ bool bHasTypes = (pStartValue->getType() == XML_n) && (pEndValue->getType() == XML_n) && (pInterval->getType() == XML_n);
+ OSL_ENSURE( bHasTypes, "PivotCacheField::importPCDFRangePr - wrong data types in grouping items" );
+ if( bHasTypes )
+ {
+ maFieldGroupModel.mfStartValue = pStartValue->getValue().get< double >();
+ maFieldGroupModel.mfEndValue = pEndValue->getValue().get< double >();
+ maFieldGroupModel.mfInterval = pInterval->getValue().get< double >();
+ }
+ }
+ }
+}
+
+void PivotCacheField::importPCDFDiscretePr( BiffInputStream& rStrm )
+{
+ sal_Int32 nCount = static_cast< sal_Int32 >( rStrm.getLength() / 2 );
+ for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
+ maDiscreteItems.push_back( rStrm.readuInt16() );
+}
+
+const PivotCacheItem* PivotCacheField::getCacheItem( sal_Int32 nItemIdx ) const
+{
+ if( hasGroupItems() )
+ return maGroupItems.getCacheItem( nItemIdx );
+ if( hasSharedItems() )
+ return maSharedItems.getCacheItem( nItemIdx );
+ return 0;
+}
+
+void PivotCacheField::getCacheItemNames( ::std::vector< OUString >& orItemNames ) const
+{
+ if( hasGroupItems() )
+ maGroupItems.getCacheItemNames( orItemNames );
+ else if( hasSharedItems() )
+ maSharedItems.getCacheItemNames( orItemNames );
+}
+
+void PivotCacheField::convertNumericGrouping( const Reference< XDataPilotField >& rxDPField ) const
+{
+ OSL_ENSURE( hasGroupItems() && hasNumericGrouping(), "PivotCacheField::convertNumericGrouping - not a numeric group field" );
+ PropertySet aPropSet( rxDPField );
+ if( hasGroupItems() && hasNumericGrouping() && aPropSet.is() )
+ {
+ DataPilotFieldGroupInfo aGroupInfo;
+ aGroupInfo.HasAutoStart = maFieldGroupModel.mbAutoStart;
+ aGroupInfo.HasAutoEnd = maFieldGroupModel.mbAutoEnd;
+ aGroupInfo.HasDateValues = sal_False;
+ aGroupInfo.Start = maFieldGroupModel.mfStartValue;
+ aGroupInfo.End = maFieldGroupModel.mfEndValue;
+ aGroupInfo.Step = maFieldGroupModel.mfInterval;
+ aGroupInfo.GroupBy = 0;
+ aPropSet.setProperty( PROP_GroupInfo, aGroupInfo );
+ }
+}
+
+OUString PivotCacheField::createDateGroupField( const Reference< XDataPilotField >& rxBaseDPField ) const
+{
+ OSL_ENSURE( hasGroupItems() && hasDateGrouping(), "PivotCacheField::createDateGroupField - not a numeric group field" );
+ Reference< XDataPilotField > xDPGroupField;
+ PropertySet aPropSet( rxBaseDPField );
+ if( hasGroupItems() && hasDateGrouping() && aPropSet.is() )
+ {
+ bool bDayRanges = (maFieldGroupModel.mnGroupBy == XML_days) && (maFieldGroupModel.mfInterval >= 2.0);
+
+ DataPilotFieldGroupInfo aGroupInfo;
+ aGroupInfo.HasAutoStart = maFieldGroupModel.mbAutoStart;
+ aGroupInfo.HasAutoEnd = maFieldGroupModel.mbAutoEnd;
+ aGroupInfo.HasDateValues = sal_True;
+ aGroupInfo.Start = getUnitConverter().calcSerialFromDateTime( maFieldGroupModel.maStartDate );
+ aGroupInfo.End = getUnitConverter().calcSerialFromDateTime( maFieldGroupModel.maEndDate );
+ aGroupInfo.Step = bDayRanges ? maFieldGroupModel.mfInterval : 0.0;
+
+ using namespace ::com::sun::star::sheet::DataPilotFieldGroupBy;
+ switch( maFieldGroupModel.mnGroupBy )
+ {
+ case XML_years: aGroupInfo.GroupBy = YEARS; break;
+ case XML_quarters: aGroupInfo.GroupBy = QUARTERS; break;
+ case XML_months: aGroupInfo.GroupBy = MONTHS; break;
+ case XML_days: aGroupInfo.GroupBy = DAYS; break;
+ case XML_hours: aGroupInfo.GroupBy = HOURS; break;
+ case XML_minutes: aGroupInfo.GroupBy = MINUTES; break;
+ case XML_seconds: aGroupInfo.GroupBy = SECONDS; break;
+ default: OSL_ENSURE( false, "PivotCacheField::convertRangeGrouping - unknown date/time interval" );
+ }
+
+ try
+ {
+ Reference< XDataPilotFieldGrouping > xDPGrouping( rxBaseDPField, UNO_QUERY_THROW );
+ xDPGroupField = xDPGrouping->createDateGroup( aGroupInfo );
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ Reference< XNamed > xFieldName( xDPGroupField, UNO_QUERY );
+ return xFieldName.is() ? xFieldName->getName() : OUString();
+}
+
+OUString PivotCacheField::createParentGroupField( const Reference< XDataPilotField >& rxBaseDPField, PivotCacheGroupItemVector& orItemNames ) const
+{
+ OSL_ENSURE( hasGroupItems() && !maDiscreteItems.empty(), "PivotCacheField::createParentGroupField - not a group field" );
+ OSL_ENSURE( maDiscreteItems.size() == orItemNames.size(), "PivotCacheField::createParentGroupField - number of item names does not match grouping info" );
+ Reference< XDataPilotFieldGrouping > xDPGrouping( rxBaseDPField, UNO_QUERY );
+ if( !xDPGrouping.is() ) return OUString();
+
+ // map the group item indexes from maGroupItems to all item indexes from maDiscreteItems
+ typedef ::std::vector< sal_Int32 > GroupItemList;
+ typedef ::std::vector< GroupItemList > GroupItemMap;
+ GroupItemMap aItemMap( maGroupItems.size() );
+ for( IndexVector::const_iterator aBeg = maDiscreteItems.begin(), aIt = aBeg, aEnd = maDiscreteItems.end(); aIt != aEnd; ++aIt )
+ if( GroupItemList* pItems = ContainerHelper::getVectorElement( aItemMap, *aIt ) )
+ pItems->push_back( static_cast< sal_Int32 >( aIt - aBeg ) );
+
+ // process all groups
+ Reference< XDataPilotField > xDPGroupField;
+ for( GroupItemMap::iterator aBeg = aItemMap.begin(), aIt = aBeg, aEnd = aItemMap.end(); aIt != aEnd; ++aIt )
+ {
+ OSL_ENSURE( !aIt->empty(), "PivotCacheField::createParentGroupField - item/group should not be empty" );
+ // if the item count is greater than 1, the item is a group of items
+ if( aIt->size() > 1 )
+ {
+ /* Insert the names of the items that are part of this group. Calc
+ expects the names of the members of the field whose members are
+ grouped (which may be the names of groups too). Excel provides
+ the names of the base field items instead (no group names
+ involved). Therefore, the passed collection of current item
+ names as they are already grouped is used here to resolve the
+ item names. */
+ ::std::vector< OUString > aMembers;
+ for( GroupItemList::iterator aBeg2 = aIt->begin(), aIt2 = aBeg2, aEnd2 = aIt->end(); aIt2 != aEnd2; ++aIt2 )
+ if( const PivotCacheGroupItem* pName = ContainerHelper::getVectorElement( orItemNames, *aIt2 ) )
+ if( ::std::find( aMembers.begin(), aMembers.end(), pName->maGroupName ) == aMembers.end() )
+ aMembers.push_back( pName->maGroupName );
+
+ /* Check again, that this is not just a group that is not grouped
+ further with other items. */
+ if( aMembers.size() > 1 ) try
+ {
+ // only the first call of createNameGroup() returns the new field
+ Reference< XDataPilotField > xDPNewField = xDPGrouping->createNameGroup( ContainerHelper::vectorToSequence( aMembers ) );
+ OSL_ENSURE( xDPGroupField.is() != xDPNewField.is(), "PivotCacheField::createParentGroupField - missing group field" );
+ if( !xDPGroupField.is() )
+ xDPGroupField = xDPNewField;
+
+ // get current grouping info
+ DataPilotFieldGroupInfo aGroupInfo;
+ PropertySet aPropSet( xDPGroupField );
+ aPropSet.getProperty( aGroupInfo, PROP_GroupInfo );
+
+ /* Find the group object and the auto-generated group name.
+ The returned field contains all groups derived from the
+ previous field if that is grouped too. To find the correct
+ group, the first item used to create the group is serached.
+ Calc provides the original item names of the base field
+ when the group is querried for its members. Its does not
+ provide the names of members that are already groups in the
+ field used to create the new groups. (Is this a bug?)
+ Therefore, a name from the passed list of original item
+ names is used to find the correct group. */
+ OUString aFirstItem;
+ if( const PivotCacheGroupItem* pName = ContainerHelper::getVectorElement( orItemNames, aIt->front() ) )
+ aFirstItem = pName->maOrigName;
+ Reference< XNamed > xGroupName;
+ OUString aAutoName;
+ Reference< XIndexAccess > xGroupsIA( aGroupInfo.Groups, UNO_QUERY_THROW );
+ for( sal_Int32 nIdx = 0, nCount = xGroupsIA->getCount(); (nIdx < nCount) && (aAutoName.getLength() == 0); ++nIdx ) try
+ {
+ Reference< XNameAccess > xItemsNA( xGroupsIA->getByIndex( nIdx ), UNO_QUERY_THROW );
+ if( xItemsNA->hasByName( aFirstItem ) )
+ {
+ xGroupName.set( xGroupsIA->getByIndex( nIdx ), UNO_QUERY_THROW );
+ aAutoName = xGroupName->getName();
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( aAutoName.getLength() > 0, "PivotCacheField::createParentGroupField - cannot find auto-generated group name" );
+
+ // get the real group name from the list of group items
+ OUString aGroupName;
+ if( const PivotCacheItem* pGroupItem = maGroupItems.getCacheItem( static_cast< sal_Int32 >( aIt - aBeg ) ) )
+ aGroupName = pGroupItem->getName();
+ OSL_ENSURE( aGroupName.getLength() > 0, "PivotCacheField::createParentGroupField - cannot find group name" );
+ if( aGroupName.getLength() == 0 )
+ aGroupName = aAutoName;
+
+ if( xGroupName.is() && (aGroupName.getLength() > 0) )
+ {
+ // replace the auto-generated group name with the real name
+ if( aAutoName != aGroupName )
+ {
+ xGroupName->setName( aGroupName );
+ aPropSet.setProperty( PROP_GroupInfo, aGroupInfo );
+ }
+ // replace original item names in passed vector with group name
+ for( GroupItemList::iterator aIt2 = aIt->begin(), aEnd2 = aIt->end(); aIt2 != aEnd2; ++aIt2 )
+ if( PivotCacheGroupItem* pName = ContainerHelper::getVectorElement( orItemNames, *aIt2 ) )
+ pName->maGroupName = aGroupName;
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ }
+ }
+
+ Reference< XNamed > xFieldName( xDPGroupField, UNO_QUERY );
+ return xFieldName.is() ? xFieldName->getName() : OUString();
+}
+
+void PivotCacheField::writeSourceHeaderCell( WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ rSheetHelper.setStringCell( rSheetHelper.getCell( CellAddress( rSheetHelper.getSheetIndex(), nCol, nRow ) ), maFieldModel.maName );
+}
+
+void PivotCacheField::writeSourceDataCell( WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow, const PivotCacheItem& rItem ) const
+{
+ bool bHasIndex = rItem.getType() == XML_x;
+ OSL_ENSURE( bHasIndex != maSharedItems.empty(), "PivotCacheField::writeSourceDataCell - shared items missing or not expected" );
+ if( bHasIndex )
+ writeSharedItemToSourceDataCell( rSheetHelper, nCol, nRow, rItem.getValue().get< sal_Int32 >() );
+ else
+ writeItemToSourceDataCell( rSheetHelper, nCol, nRow, rItem );
+}
+
+void PivotCacheField::importPCRecordItem( RecordInputStream& rStrm, WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ if( hasSharedItems() )
+ {
+ writeSharedItemToSourceDataCell( rSheetHelper, nCol, nRow, rStrm.readInt32() );
+ }
+ else
+ {
+ PivotCacheItem aItem;
+ if( maSharedItemsModel.mbIsNumeric )
+ aItem.readDouble( rStrm );
+ else if( maSharedItemsModel.mbHasDate && !maSharedItemsModel.mbHasString )
+ aItem.readDate( rStrm );
+ else
+ aItem.readString( rStrm );
+ writeItemToSourceDataCell( rSheetHelper, nCol, nRow, aItem );
+ }
+}
+
+void PivotCacheField::importPCItemIndex( BiffInputStream& rStrm, WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ OSL_ENSURE( hasSharedItems(), "PivotCacheField::importPCItemIndex - invalid call, no shared items found" );
+ sal_Int32 nIndex = maSharedItemsModel.mbHasLongIndexes ? rStrm.readuInt16() : rStrm.readuInt8();
+ writeSharedItemToSourceDataCell( rSheetHelper, nCol, nRow, nIndex );
+}
+
+// private --------------------------------------------------------------------
+
+void PivotCacheField::writeItemToSourceDataCell( WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow, const PivotCacheItem& rItem ) const
+{
+ if( rItem.getType() != XML_m )
+ {
+ Reference< XCell > xCell = rSheetHelper.getCell( CellAddress( rSheetHelper.getSheetIndex(), nCol, nRow ) );
+ if( xCell.is() ) switch( rItem.getType() )
+ {
+ case XML_s: rSheetHelper.setStringCell( xCell, rItem.getValue().get< OUString >() ); break;
+ case XML_n: xCell->setValue( rItem.getValue().get< double >() ); break;
+ case XML_i: xCell->setValue( rItem.getValue().get< sal_Int16 >() ); break;
+ case XML_d: rSheetHelper.setDateTimeCell( xCell, rItem.getValue().get< DateTime >() ); break;
+ case XML_b: rSheetHelper.setBooleanCell( xCell, rItem.getValue().get< bool >() ); break;
+ case XML_e: rSheetHelper.setErrorCell( xCell, static_cast< sal_uInt8 >( rItem.getValue().get< sal_Int32 >() ) ); break;
+ default: OSL_ENSURE( false, "PivotCacheField::writeItemToSourceDataCell - unexpected item data type" );
+ }
+ }
+}
+
+void PivotCacheField::writeSharedItemToSourceDataCell(
+ WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nItemIdx ) const
+{
+ if( const PivotCacheItem* pCacheItem = maSharedItems.getCacheItem( nItemIdx ) )
+ writeItemToSourceDataCell( rSheetHelper, nCol, nRow, *pCacheItem );
+}
+
+// ============================================================================
+
+PCDefinitionModel::PCDefinitionModel() :
+ mfRefreshedDate( 0.0 ),
+ mnRecords( 0 ),
+ mnMissItemsLimit( 0 ),
+ mnDatabaseFields( 0 ),
+ mbInvalid( false ),
+ mbSaveData( true ),
+ mbRefreshOnLoad( false ),
+ mbOptimizeMemory( false ),
+ mbEnableRefresh( true ),
+ mbBackgroundQuery( false ),
+ mbUpgradeOnRefresh( false ),
+ mbTupleCache( false ),
+ mbSupportSubquery( false ),
+ mbSupportDrill( false )
+{
+}
+
+// ----------------------------------------------------------------------------
+
+PCSourceModel::PCSourceModel() :
+ mnSourceType( XML_TOKEN_INVALID ),
+ mnConnectionId( 0 )
+{
+}
+
+// ----------------------------------------------------------------------------
+
+PCWorksheetSourceModel::PCWorksheetSourceModel()
+{
+ maRange.StartColumn = maRange.StartRow = maRange.EndColumn = maRange.EndRow = -1;
+}
+
+// ----------------------------------------------------------------------------
+
+PivotCache::PivotCache( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mbValidSource( false ),
+ mbDummySheet( false )
+{
+}
+
+void PivotCache::importPivotCacheDefinition( const AttributeList& rAttribs )
+{
+ maDefModel.maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ maDefModel.maRefreshedBy = rAttribs.getXString( XML_refreshedBy, OUString() );
+ maDefModel.mfRefreshedDate = rAttribs.getDouble( XML_refreshedDate, 0.0 );
+ maDefModel.mnRecords = rAttribs.getInteger( XML_recordCount, 0 );
+ maDefModel.mnMissItemsLimit = rAttribs.getInteger( XML_missingItemsLimit, 0 );
+ maDefModel.mbInvalid = rAttribs.getBool( XML_invalid, false );
+ maDefModel.mbSaveData = rAttribs.getBool( XML_saveData, true );
+ maDefModel.mbRefreshOnLoad = rAttribs.getBool( XML_refreshOnLoad, false );
+ maDefModel.mbOptimizeMemory = rAttribs.getBool( XML_optimizeMemory, false );
+ maDefModel.mbEnableRefresh = rAttribs.getBool( XML_enableRefresh, true );
+ maDefModel.mbBackgroundQuery = rAttribs.getBool( XML_backgroundQuery, false );
+ maDefModel.mbUpgradeOnRefresh = rAttribs.getBool( XML_upgradeOnRefresh, false );
+ maDefModel.mbTupleCache = rAttribs.getBool( XML_tupleCache, false );
+ maDefModel.mbSupportSubquery = rAttribs.getBool( XML_supportSubquery, false );
+ maDefModel.mbSupportDrill = rAttribs.getBool( XML_supportAdvancedDrill, false );
+}
+
+void PivotCache::importCacheSource( const AttributeList& rAttribs )
+{
+ maSourceModel.mnSourceType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID );
+ maSourceModel.mnConnectionId = rAttribs.getInteger( XML_connectionId, 0 );
+}
+
+void PivotCache::importWorksheetSource( const AttributeList& rAttribs, const Relations& rRelations )
+{
+ maSheetSrcModel.maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ maSheetSrcModel.maSheet = rAttribs.getXString( XML_sheet, OUString() );
+ maSheetSrcModel.maDefName = rAttribs.getXString( XML_name, OUString() );
+
+ // resolve URL of external document
+ maTargetUrl = rRelations.getExternalTargetFromRelId( maSheetSrcModel.maRelId );
+ // store range address unchecked with sheet index 0, will be resolved/checked later
+ getAddressConverter().convertToCellRangeUnchecked( maSheetSrcModel.maRange, rAttribs.getString( XML_ref, OUString() ), 0 );
+}
+
+void PivotCache::importPCDefinition( RecordInputStream& rStrm )
+{
+ sal_uInt8 nFlags1, nFlags2;
+ rStrm.skip( 3 ); // create/refresh version id's
+ rStrm >> nFlags1 >> maDefModel.mnMissItemsLimit >> maDefModel.mfRefreshedDate >> nFlags2 >> maDefModel.mnRecords;
+ if( getFlag( nFlags2, OOBIN_PCDEFINITION_HASUSERNAME ) )
+ rStrm >> maDefModel.maRefreshedBy;
+ if( getFlag( nFlags2, OOBIN_PCDEFINITION_HASRELID ) )
+ rStrm >> maDefModel.maRelId;
+
+ maDefModel.mbInvalid = getFlag( nFlags1, OOBIN_PCDEFINITION_INVALID );
+ maDefModel.mbSaveData = getFlag( nFlags1, OOBIN_PCDEFINITION_SAVEDATA );
+ maDefModel.mbRefreshOnLoad = getFlag( nFlags1, OOBIN_PCDEFINITION_REFRESHONLOAD );
+ maDefModel.mbOptimizeMemory = getFlag( nFlags1, OOBIN_PCDEFINITION_OPTIMIZEMEMORY );
+ maDefModel.mbEnableRefresh = getFlag( nFlags1, OOBIN_PCDEFINITION_ENABLEREFRESH );
+ maDefModel.mbBackgroundQuery = getFlag( nFlags1, OOBIN_PCDEFINITION_BACKGROUNDQUERY );
+ maDefModel.mbUpgradeOnRefresh = getFlag( nFlags1, OOBIN_PCDEFINITION_UPGRADEONREFR );
+ maDefModel.mbTupleCache = getFlag( nFlags1, OOBIN_PCDEFINITION_TUPELCACHE );
+ maDefModel.mbSupportSubquery = getFlag( nFlags2, OOBIN_PCDEFINITION_SUPPORTSUBQUERY );
+ maDefModel.mbSupportDrill = getFlag( nFlags2, OOBIN_PCDEFINITION_SUPPORTDRILL );
+}
+
+void PivotCache::importPCDSource( RecordInputStream& rStrm )
+{
+ sal_Int32 nSourceType;
+ rStrm >> nSourceType >> maSourceModel.mnConnectionId;
+ static const sal_Int32 spnSourceTypes[] = { XML_worksheet, XML_external, XML_consolidation, XML_scenario };
+ maSourceModel.mnSourceType = STATIC_ARRAY_SELECT( spnSourceTypes, nSourceType, XML_TOKEN_INVALID );
+}
+
+void PivotCache::importPCDSheetSource( RecordInputStream& rStrm, const Relations& rRelations )
+{
+ sal_uInt8 nIsDefName, nIsBuiltinName, nFlags;
+ rStrm >> nIsDefName >> nIsBuiltinName >> nFlags;
+ if( getFlag( nFlags, OOBIN_PCDWBSOURCE_HASSHEET ) )
+ rStrm >> maSheetSrcModel.maSheet;
+ if( getFlag( nFlags, OOBIN_PCDWBSOURCE_HASRELID ) )
+ rStrm >> maSheetSrcModel.maRelId;
+
+ // read cell range or defined name
+ if( nIsDefName == 0 )
+ {
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ // store range address unchecked with sheet index 0, will be resolved/checked later
+ getAddressConverter().convertToCellRangeUnchecked( maSheetSrcModel.maRange, aBinRange, 0 );
+ }
+ else
+ {
+ rStrm >> maSheetSrcModel.maDefName;
+ if( nIsBuiltinName != 0 )
+ maSheetSrcModel.maDefName = CREATE_OUSTRING( "_xlnm." ) + maSheetSrcModel.maDefName;
+ }
+
+ // resolve URL of external document
+ maTargetUrl = rRelations.getExternalTargetFromRelId( maSheetSrcModel.maRelId );
+}
+
+void PivotCache::importPCDSource( BiffInputStream& rStrm )
+{
+ switch( rStrm.readuInt16() )
+ {
+ case BIFF_PCDSOURCE_WORKSHEET:
+ {
+ maSourceModel.mnSourceType = XML_worksheet;
+ sal_uInt16 nNextRecId = rStrm.getNextRecId();
+ switch( nNextRecId )
+ {
+ case BIFF_ID_DCONREF: if( rStrm.startNextRecord() ) importDConRef( rStrm ); break;
+ case BIFF_ID_DCONNAME: if( rStrm.startNextRecord() ) importDConName( rStrm ); break;
+ case BIFF_ID_DCONBINAME: if( rStrm.startNextRecord() ) importDConBIName( rStrm ); break;
+ }
+ }
+ break;
+ case BIFF_PCDSOURCE_EXTERNAL:
+ maSourceModel.mnSourceType = XML_external;
+ break;
+ case BIFF_PCDSOURCE_CONSOLIDATION:
+ maSourceModel.mnSourceType = XML_consolidation;
+ break;
+ case BIFF_PCDSOURCE_SCENARIO:
+ maSourceModel.mnSourceType = XML_scenario;
+ break;
+ default:
+ maSourceModel.mnSourceType = XML_TOKEN_INVALID;
+ }
+}
+
+void PivotCache::importPCDefinition( BiffInputStream& rStrm )
+{
+ sal_uInt16 nFlags, nUserNameLen;
+ rStrm >> maDefModel.mnRecords;
+ rStrm.skip( 2 ); // repeated cache ID
+ rStrm >> nFlags;
+ rStrm.skip( 2 ); // unused
+ rStrm >> maDefModel.mnDatabaseFields;
+ rStrm.skip( 6 ); // total field count, report record count, (repeated) cache type
+ rStrm >> nUserNameLen;
+ if( nUserNameLen != BIFF_PC_NOSTRING )
+ maDefModel.maRefreshedBy = (getBiff() == BIFF8) ?
+ rStrm.readUniString( nUserNameLen ) :
+ rStrm.readCharArrayUC( nUserNameLen, getTextEncoding() );
+
+ maDefModel.mbInvalid = getFlag( nFlags, BIFF_PCDEFINITION_INVALID );
+ maDefModel.mbSaveData = getFlag( nFlags, BIFF_PCDEFINITION_SAVEDATA );
+ maDefModel.mbRefreshOnLoad = getFlag( nFlags, BIFF_PCDEFINITION_REFRESHONLOAD );
+ maDefModel.mbOptimizeMemory = getFlag( nFlags, BIFF_PCDEFINITION_OPTIMIZEMEMORY );
+ maDefModel.mbEnableRefresh = getFlag( nFlags, BIFF_PCDEFINITION_ENABLEREFRESH );
+ maDefModel.mbBackgroundQuery = getFlag( nFlags, BIFF_PCDEFINITION_BACKGROUNDQUERY );
+
+ if( (rStrm.getNextRecId() == BIFF_ID_PCDEFINITION2) && rStrm.startNextRecord() )
+ rStrm >> maDefModel.mfRefreshedDate;
+}
+
+PivotCacheField& PivotCache::createCacheField( bool bInitDatabaseField )
+{
+ bool bIsDatabaseField = !bInitDatabaseField || (maFields.size() < maDefModel.mnDatabaseFields);
+ PivotCacheFieldVector::value_type xCacheField( new PivotCacheField( *this, bIsDatabaseField ) );
+ maFields.push_back( xCacheField );
+ return *xCacheField;
+}
+
+void PivotCache::finalizeImport()
+{
+ // collect all fields that are based on source data (needed to finalize source data below)
+ OSL_ENSURE( !maFields.empty(), "PivotCache::finalizeImport - no pivot cache fields found" );
+ for( PivotCacheFieldVector::const_iterator aIt = maFields.begin(), aEnd = maFields.end(); aIt != aEnd; ++aIt )
+ {
+ if( (*aIt)->isDatabaseField() )
+ {
+ OSL_ENSURE( (aIt == maFields.begin()) || (*(aIt - 1))->isDatabaseField(),
+ "PivotCache::finalizeImport - database field follows a calculated field" );
+ maDatabaseIndexes.push_back( static_cast< sal_Int32 >( maDatabaseFields.size() ) );
+ maDatabaseFields.push_back( *aIt );
+ }
+ else
+ {
+ maDatabaseIndexes.push_back( -1 );
+ }
+ }
+ OSL_ENSURE( !maDatabaseFields.empty(), "PivotCache::finalizeImport - no pivot cache source fields found" );
+
+ // finalize source data depending on source type
+ switch( maSourceModel.mnSourceType )
+ {
+ case XML_worksheet:
+ {
+ // decide whether an external document is used
+ bool bInternal = (maTargetUrl.getLength() == 0) && (maSheetSrcModel.maRelId.getLength() == 0);
+ bool bExternal = maTargetUrl.getLength() > 0; // relation ID may be empty, e.g. BIFF import
+ OSL_ENSURE( bInternal || bExternal, "PivotCache::finalizeImport - invalid external document URL" );
+ if( bInternal )
+ finalizeInternalSheetSource();
+ else if( bExternal )
+ finalizeExternalSheetSource();
+ }
+ break;
+
+ // currently, we only support worksheet data sources
+ case XML_external:
+ break;
+ case XML_consolidation:
+ break;
+ case XML_scenario:
+ break;
+ }
+}
+
+sal_Int32 PivotCache::getCacheFieldCount() const
+{
+ return static_cast< sal_Int32 >( maFields.size() );
+}
+
+const PivotCacheField* PivotCache::getCacheField( sal_Int32 nFieldIdx ) const
+{
+ return maFields.get( nFieldIdx ).get();
+}
+
+sal_Int32 PivotCache::getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const
+{
+ return ContainerHelper::getVectorElement< sal_Int32 >( maDatabaseIndexes, nFieldIdx, -1 );
+}
+
+void PivotCache::writeSourceHeaderCells( WorksheetHelper& rSheetHelper ) const
+{
+ OSL_ENSURE( static_cast< size_t >( maSheetSrcModel.maRange.EndColumn - maSheetSrcModel.maRange.StartColumn + 1 ) == maDatabaseFields.size(),
+ "PivotCache::writeSourceHeaderCells - source cell range width does not match number of source fields" );
+ sal_Int32 nCol = maSheetSrcModel.maRange.StartColumn;
+ sal_Int32 nMaxCol = getAddressConverter().getMaxApiAddress().Column;
+ sal_Int32 nRow = maSheetSrcModel.maRange.StartRow;
+ for( PivotCacheFieldVector::const_iterator aIt = maDatabaseFields.begin(), aEnd = maDatabaseFields.end(); (aIt != aEnd) && (nCol <= nMaxCol); ++aIt, ++nCol )
+ (*aIt)->writeSourceHeaderCell( rSheetHelper, nCol, nRow );
+}
+
+void PivotCache::writeSourceDataCell( WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow, const PivotCacheItem& rItem ) const
+{
+ OSL_ENSURE( (0 <= nCol) && (nCol <= maSheetSrcModel.maRange.EndColumn - maSheetSrcModel.maRange.StartColumn), "PivotCache::writeSourceDataCell - invalid column index" );
+ OSL_ENSURE( (0 < nRow) && (nRow <= maSheetSrcModel.maRange.EndRow - maSheetSrcModel.maRange.StartRow), "PivotCache::writeSourceDataCell - invalid row index" );
+ if( const PivotCacheField* pCacheField = maDatabaseFields.get( nCol ).get() )
+ pCacheField->writeSourceDataCell( rSheetHelper, maSheetSrcModel.maRange.StartColumn + nCol, maSheetSrcModel.maRange.StartRow + nRow, rItem );
+}
+
+void PivotCache::importPCRecord( RecordInputStream& rStrm, WorksheetHelper& rSheetHelper, sal_Int32 nRow ) const
+{
+ OSL_ENSURE( (0 < nRow) && (nRow <= maSheetSrcModel.maRange.EndRow - maSheetSrcModel.maRange.StartRow), "PivotCache::importPCRecord - invalid row index" );
+ sal_Int32 nCol = maSheetSrcModel.maRange.StartColumn;
+ sal_Int32 nMaxCol = getAddressConverter().getMaxApiAddress().Column;
+ nRow += maSheetSrcModel.maRange.StartRow;
+ for( PivotCacheFieldVector::const_iterator aIt = maDatabaseFields.begin(), aEnd = maDatabaseFields.end(); !rStrm.isEof() && (aIt != aEnd) && (nCol <= nMaxCol); ++aIt, ++nCol )
+ (*aIt)->importPCRecordItem( rStrm, rSheetHelper, nCol, nRow );
+}
+
+void PivotCache::importPCItemIndexList( BiffInputStream& rStrm, WorksheetHelper& rSheetHelper, sal_Int32 nRow ) const
+{
+ OSL_ENSURE( (0 < nRow) && (nRow <= maSheetSrcModel.maRange.EndRow - maSheetSrcModel.maRange.StartRow), "PivotCache::importPCItemIndexList - invalid row index" );
+ sal_Int32 nCol = maSheetSrcModel.maRange.StartColumn;
+ sal_Int32 nMaxCol = getAddressConverter().getMaxApiAddress().Column;
+ nRow += maSheetSrcModel.maRange.StartRow;
+ for( PivotCacheFieldVector::const_iterator aIt = maDatabaseFields.begin(), aEnd = maDatabaseFields.end(); !rStrm.isEof() && (aIt != aEnd) && (nCol <= nMaxCol); ++aIt, ++nCol )
+ if( (*aIt)->hasSharedItems() )
+ (*aIt)->importPCItemIndex( rStrm, rSheetHelper, nCol, nRow );
+}
+
+// private --------------------------------------------------------------------
+
+void PivotCache::importDConRef( BiffInputStream& rStrm )
+{
+ BinRange aBinRange;
+ aBinRange.read( rStrm, false ); // always 8-bit column indexes
+ // store range address unchecked with sheet index 0, will be resolved/checked later
+ getAddressConverter().convertToCellRangeUnchecked( maSheetSrcModel.maRange, aBinRange, 0 );
+
+ // the URL with (required) sheet name and optional URL of an external document
+ importDConUrl( rStrm );
+ OSL_ENSURE( maSheetSrcModel.maSheet.getLength() > 0, "PivotCache::importDConRef - missing sheet name" );
+}
+
+void PivotCache::importDConName( BiffInputStream& rStrm )
+{
+ maSheetSrcModel.maDefName = (getBiff() == BIFF8) ? rStrm.readUniString() : rStrm.readByteStringUC( false, getTextEncoding() );
+ OSL_ENSURE( maSheetSrcModel.maDefName.getLength() > 0, "PivotCache::importDConName - missing defined name" );
+ importDConUrl( rStrm );
+}
+
+void PivotCache::importDConBIName( BiffInputStream& rStrm )
+{
+ sal_uInt8 nNameId = rStrm.readuInt8();
+ rStrm.skip( 3 );
+ maSheetSrcModel.maDefName = OUString( sal_Unicode( nNameId ) );
+ importDConUrl( rStrm );
+}
+
+void PivotCache::importDConUrl( BiffInputStream& rStrm )
+{
+ // the URL with sheet name and optional URL of an external document
+ OUString aEncodedUrl;
+ if( getBiff() == BIFF8 )
+ {
+ // empty string does not contain a flags byte, cannot use simple readUniString() here...
+ sal_uInt16 nChars = rStrm.readuInt16();
+ if( nChars > 0 )
+ aEncodedUrl = rStrm.readUniString( nChars );
+ }
+ else
+ {
+ aEncodedUrl = rStrm.readByteStringUC( false, getTextEncoding() );
+ }
+
+ if( aEncodedUrl.getLength() > 0 )
+ {
+ OUString aClassName;
+ getAddressConverter().parseBiffTargetUrl( aClassName, maTargetUrl, maSheetSrcModel.maSheet, aEncodedUrl, true );
+ }
+}
+
+void PivotCache::finalizeInternalSheetSource()
+{
+ // resolve sheet name to sheet index
+ sal_Int16 nSheet = getWorksheets().getCalcSheetIndex( maSheetSrcModel.maSheet );
+
+ // if cache is based on a defined name or table, try to resolve to cell range
+ if( maSheetSrcModel.maDefName.getLength() > 0 )
+ {
+ // local or global defined name
+ if( const DefinedName* pDefName = getDefinedNames().getByModelName( maSheetSrcModel.maDefName, nSheet ).get() )
+ {
+ mbValidSource = pDefName->getAbsoluteRange( maSheetSrcModel.maRange );
+ }
+ // table
+ else if( const Table* pTable = getTables().getTable( maSheetSrcModel.maDefName ).get() )
+ {
+ // get original range from table, but exclude the totals row(s)
+ maSheetSrcModel.maRange = pTable->getOriginalRange();
+ mbValidSource = (pTable->getHeight() - pTable->getTotalsRows()) > 1;
+ if( mbValidSource )
+ maSheetSrcModel.maRange.EndRow -= pTable->getTotalsRows();
+ }
+ }
+ // else try the cell range (if the sheet exists)
+ else if( nSheet >= 0 )
+ {
+ // insert sheet index into the range, range address will be checked below
+ maSheetSrcModel.maRange.Sheet = nSheet;
+ mbValidSource = true;
+ }
+ // else sheet has been deleted, generate the source data from cache
+ else if( maSheetSrcModel.maSheet.getLength() > 0 )
+ {
+ prepareSourceDataSheet();
+ // return here to skip the source range check below
+ return;
+ }
+
+ // check range location, do not allow ranges that overflow the sheet partly
+ mbValidSource = mbValidSource &&
+ getAddressConverter().checkCellRange( maSheetSrcModel.maRange, false, true ) &&
+ (maSheetSrcModel.maRange.StartRow < maSheetSrcModel.maRange.EndRow);
+}
+
+void PivotCache::finalizeExternalSheetSource()
+{
+ /* If pivot cache is based on external sheet data, try to restore sheet
+ data from cache records. No support for external defined names or tables,
+ sheet name and path to cache records fragment (OOX only) are required. */
+ bool bHasRelation = (getFilterType() == FILTER_BIFF) || (maDefModel.maRelId.getLength() > 0);
+ if( bHasRelation && (maSheetSrcModel.maDefName.getLength() == 0) && (maSheetSrcModel.maSheet.getLength() > 0) )
+ prepareSourceDataSheet();
+}
+
+void PivotCache::prepareSourceDataSheet()
+{
+ // data will be inserted in top-left cell, sheet index is still set to 0 (will be set below)
+ maSheetSrcModel.maRange.EndColumn -= maSheetSrcModel.maRange.StartColumn;
+ maSheetSrcModel.maRange.StartColumn = 0;
+ maSheetSrcModel.maRange.EndRow -= maSheetSrcModel.maRange.StartRow;
+ maSheetSrcModel.maRange.StartRow = 0;
+ // check range location, do not allow ranges that overflow the sheet partly
+ if( getAddressConverter().checkCellRange( maSheetSrcModel.maRange, false, true ) )
+ {
+ OUString aSheetName = CREATE_OUSTRING( "DPCache_" ) + maSheetSrcModel.maSheet;
+ maSheetSrcModel.maRange.Sheet = getWorksheets().insertEmptySheet( aSheetName, false );
+ mbValidSource = mbDummySheet = maSheetSrcModel.maRange.Sheet >= 0;
+ }
+}
+
+// ============================================================================
+
+PivotCacheBuffer::PivotCacheBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void PivotCacheBuffer::registerPivotCacheFragment( sal_Int32 nCacheId, const OUString& rFragmentPath )
+{
+ OSL_ENSURE( nCacheId >= 0, "PivotCacheBuffer::registerPivotCacheFragment - invalid pivot cache identifier" );
+ OSL_ENSURE( maFragmentPaths.count( nCacheId ) == 0, "PivotCacheBuffer::registerPivotCacheFragment - fragment path exists already" );
+ if( (nCacheId >= 0) && (rFragmentPath.getLength() > 0) )
+ maFragmentPaths[ nCacheId ] = rFragmentPath;
+}
+
+void PivotCacheBuffer::importPivotCacheRef( BiffInputStream& rStrm )
+{
+ // read the PIVOTCACHE record that contains the stream ID
+ sal_Int32 nCacheId = rStrm.readuInt16();
+ OSL_ENSURE( maFragmentPaths.count( nCacheId ) == 0, "PivotCacheBuffer::importPivotCacheRef - cache stream exists already" );
+ OUStringBuffer aStrmName;
+ static const sal_Unicode spcHexChars[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
+ for( sal_uInt8 nBit = 0; nBit < 16; nBit += 4 )
+ aStrmName.insert( 0, spcHexChars[ extractValue< size_t >( nCacheId, nBit, 4 ) ] );
+ aStrmName.insert( 0, (getBiff() == BIFF8) ? CREATE_OUSTRING( "_SX_DB_CUR/" ) : CREATE_OUSTRING( "_SX_DB/" ) );
+ maFragmentPaths[ nCacheId ] = aStrmName.makeStringAndClear();
+
+ // try to read PCDSOURCE record (will read following data location records too)
+ sal_uInt16 nNextRecId = rStrm.getNextRecId();
+ OSL_ENSURE( nNextRecId == BIFF_ID_PCDSOURCE, "PivotCacheBuffer::importPivotCacheRef - PCDSOURCE record expected" );
+ if( (nNextRecId == BIFF_ID_PCDSOURCE) && rStrm.startNextRecord() )
+ createPivotCache( nCacheId ).importPCDSource( rStrm );
+}
+
+PivotCache* PivotCacheBuffer::importPivotCacheFragment( sal_Int32 nCacheId )
+{
+ switch( getFilterType() )
+ {
+ /* BIFF filter: Pivot table provides 0-based index into list of pivot
+ cache source links (PIVOTCACHE/PCDSOURCE/... record blocks in
+ workbook stream). First, this index has to be resolved to the cache
+ identifier that is used to manage the cache stream names (the
+ maFragmentPaths member). The cache object itself exists already
+ before the first call for the cache source index (see
+ PivotCacheBuffer::importPivotCacheRef() above), because source data
+ link is part of workbook data, not of the cache stream. To detect
+ subsequent calls with an already initialized cache, the entry in
+ maFragmentPaths will be removed after reading the cache stream. */
+ case FILTER_BIFF:
+ {
+ /* Resolve cache index to cache identifier and try to find pivot
+ cache. Cache must exist already for a valid cache index. */
+ nCacheId = ContainerHelper::getVectorElement< sal_Int32 >( maCacheIds, nCacheId, -1 );
+ PivotCache* pCache = maCaches.get( nCacheId ).get();
+ if( !pCache )
+ return 0;
+
+ /* Try to find fragment path entry (stream name). If missing, the
+ stream has been read already, and the cache can be returned. */
+ FragmentPathMap::iterator aIt = maFragmentPaths.find( nCacheId );
+ if( aIt != maFragmentPaths.end() )
+ {
+ /* Import the cache stream. This may create a dummy data sheet
+ for external sheet sources. */
+ BiffPivotCacheFragment( *this, aIt->second, *pCache ).importFragment();
+ // remove the fragment entry to mark that the cache is initialized
+ maFragmentPaths.erase( aIt );
+ }
+ return pCache;
+ }
+
+ /* OOX/OOBIN filter: On first call for the cache ID, the pivot cache
+ object is created and inserted into maCaches. Then, the cache
+ definition fragment is read and the cache is returned. On
+ subsequent calls, the created cache will be found in maCaches and
+ returned immediately. */
+ case FILTER_OOX:
+ {
+ // try to find an imported pivot cache
+ if( PivotCache* pCache = maCaches.get( nCacheId ).get() )
+ return pCache;
+
+ // check if a fragment path exists for the passed cache identifier
+ FragmentPathMap::iterator aIt = maFragmentPaths.find( nCacheId );
+ if( aIt == maFragmentPaths.end() )
+ return 0;
+
+ /* Import the cache fragment. This may create a dummy data sheet
+ for external sheet sources. */
+ PivotCache& rCache = createPivotCache( nCacheId );
+ importOoxFragment( new OoxPivotCacheDefinitionFragment( *this, aIt->second, rCache ) );
+ return &rCache;
+ }
+
+ case FILTER_UNKNOWN:
+ OSL_ENSURE( false, "PivotCacheBuffer::importPivotCacheFragment - unknown filter type" );
+ }
+ return 0;
+}
+
+PivotCache& PivotCacheBuffer::createPivotCache( sal_Int32 nCacheId )
+{
+ maCacheIds.push_back( nCacheId );
+ PivotCacheMap::mapped_type& rxCache = maCaches[ nCacheId ];
+ rxCache.reset( new PivotCache( *this ) );
+ return *rxCache;
+}
+
+// ============================================================================
+
+} // namespace xls
+} // namespace oox
+