diff options
author | Daniel Rentz [dr] <daniel.rentz@oracle.com> | 2010-11-04 11:03:35 +0100 |
---|---|---|
committer | Daniel Rentz [dr] <daniel.rentz@oracle.com> | 2010-11-04 11:03:35 +0100 |
commit | 0b66e33ee50cd1c77bd9b1073ab298bac03bcfb7 (patch) | |
tree | 3debbe6b1d0f39ee0d2d6e8d813438b9abcde483 /oox/source/xls | |
parent | 65a1b13469cf7c2497f70a6a23363fe7cd643121 (diff) |
dr77: #i102872# handle new filter operators from css.sheet.FilterOperator2, rewrite OOXML autofilter import, add BIFF5/8/12 support
Diffstat (limited to 'oox/source/xls')
-rwxr-xr-x | oox/source/xls/autofilterbuffer.cxx | 856 | ||||
-rw-r--r-- | oox/source/xls/autofiltercontext.cxx | 731 | ||||
-rwxr-xr-x | oox/source/xls/connectionsbuffer.cxx | 55 | ||||
-rw-r--r-- | oox/source/xls/connectionsfragment.cxx | 51 | ||||
-rw-r--r-- | oox/source/xls/defnamesbuffer.cxx | 158 | ||||
-rw-r--r-- | oox/source/xls/makefile.mk | 3 | ||||
-rw-r--r-- | oox/source/xls/pivotcachefragment.cxx | 5 | ||||
-rw-r--r-- | oox/source/xls/pivottablefragment.cxx | 4 | ||||
-rw-r--r-- | oox/source/xls/querytablebuffer.cxx | 20 | ||||
-rw-r--r-- | oox/source/xls/querytablefragment.cxx | 4 | ||||
-rw-r--r-- | oox/source/xls/sheetdatacontext.cxx | 7 | ||||
-rw-r--r-- | oox/source/xls/tablebuffer.cxx | 50 | ||||
-rw-r--r-- | oox/source/xls/tablefragment.cxx | 33 | ||||
-rw-r--r-- | oox/source/xls/workbookhelper.cxx | 50 | ||||
-rw-r--r-- | oox/source/xls/worksheetfragment.cxx | 19 | ||||
-rw-r--r-- | oox/source/xls/worksheethelper.cxx | 11 |
16 files changed, 1204 insertions, 853 deletions
diff --git a/oox/source/xls/autofilterbuffer.cxx b/oox/source/xls/autofilterbuffer.cxx new file mode 100755 index 000000000000..15cb2f726327 --- /dev/null +++ b/oox/source/xls/autofilterbuffer.cxx @@ -0,0 +1,856 @@ +/************************************************************************* + * + * 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/autofilterbuffer.hxx" + +#include <com/sun/star/sheet/FilterConnection.hpp> +#include <com/sun/star/sheet/FilterOperator2.hpp> +#include <com/sun/star/sheet/TableFilterField2.hpp> +#include <com/sun/star/sheet/XDatabaseRange.hpp> +#include <com/sun/star/sheet/XSheetFilterDescriptor2.hpp> +#include <com/sun/star/table/TableOrientation.hpp> +#include <rtl/ustrbuf.hxx> +#include "oox/core/namespaces.hxx" +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/defnamesbuffer.hxx" +#include "properties.hxx" +#include "tokens.hxx" + +namespace oox { +namespace xls { + +using namespace ::com::sun::star::sheet; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::uno; + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +// ============================================================================ + +namespace { + +const sal_uInt8 BIFF12_TOP10FILTER_TOP = 0x01; +const sal_uInt8 BIFF12_TOP10FILTER_PERCENT = 0x02; + +const sal_uInt16 BIFF12_FILTERCOLUMN_HIDDENBUTTON = 0x0001; +const sal_uInt16 BIFF12_FILTERCOLUMN_SHOWBUTTON = 0x0002; + +const sal_uInt16 BIFF_FILTERCOLUMN_OR = 0x0001; +const sal_uInt16 BIFF_FILTERCOLUMN_TOP10FILTER = 0x0010; +const sal_uInt16 BIFF_FILTERCOLUMN_TOP = 0x0020; +const sal_uInt16 BIFF_FILTERCOLUMN_PERCENT = 0x0040; + +const sal_uInt8 BIFF_FILTER_DATATYPE_NONE = 0; +const sal_uInt8 BIFF_FILTER_DATATYPE_RK = 2; +const sal_uInt8 BIFF_FILTER_DATATYPE_DOUBLE = 4; +const sal_uInt8 BIFF_FILTER_DATATYPE_STRING = 6; +const sal_uInt8 BIFF_FILTER_DATATYPE_BOOLEAN = 8; +const sal_uInt8 BIFF_FILTER_DATATYPE_EMPTY = 12; +const sal_uInt8 BIFF_FILTER_DATATYPE_NOTEMPTY = 14; + +// ---------------------------------------------------------------------------- + +bool lclGetApiOperatorFromToken( sal_Int32& rnApiOperator, sal_Int32 nToken ) +{ + switch( nToken ) + { + case XML_lessThan: rnApiOperator = FilterOperator2::NOT_EQUAL; return true; + case XML_equal: rnApiOperator = FilterOperator2::EQUAL; return true; + case XML_lessThanOrEqual: rnApiOperator = FilterOperator2::LESS_EQUAL; return true; + case XML_greaterThan: rnApiOperator = FilterOperator2::GREATER; return true; + case XML_notEqual: rnApiOperator = FilterOperator2::NOT_EQUAL; return true; + case XML_greaterThanOrEqual: rnApiOperator = FilterOperator2::GREATER_EQUAL; return true; + } + return false; +} + +/** Removes leading asterisk characters from the passed string. + @return True = at least one asterisk character has been removed. */ +bool lclTrimLeadingAsterisks( OUString& rValue ) +{ + sal_Int32 nLength = rValue.getLength(); + sal_Int32 nPos = 0; + while( (nPos < nLength) && (rValue[ nPos ] == '*') ) + ++nPos; + if( nPos > 0 ) + { + rValue = rValue.copy( nPos ); + return true; + } + return false; +} + +/** Removes trailing asterisk characters from the passed string. + @return True = at least one asterisk character has been removed. */ +bool lclTrimTrailingAsterisks( OUString& rValue ) +{ + sal_Int32 nLength = rValue.getLength(); + sal_Int32 nPos = nLength; + while( (nPos > 0) && (rValue[ nPos - 1 ] == '*') ) + --nPos; + if( nPos < nLength ) + { + rValue = rValue.copy( 0, nPos ); + return true; + } + return false; +} + +/** Converts wildcard characters '*' and '?' to regular expressions and quotes + RE meta characters. + @return True = passed string has been changed (RE needs to be enabled). */ +bool lclConvertWildcardsToRegExp( OUString& rValue ) +{ + // check existence of the wildcard characters '*' and '?' + if( (rValue.getLength() > 0) && ((rValue.indexOf( '*' ) >= 0) || (rValue.indexOf( '?' ) >= 0)) ) + { + OUStringBuffer aBuffer; + aBuffer.ensureCapacity( rValue.getLength() + 5 ); + const sal_Unicode* pcChar = rValue.getStr(); + const sal_Unicode* pcEnd = pcChar + rValue.getLength(); + for( ; pcChar < pcEnd; ++pcChar ) + { + switch( *pcChar ) + { + case '?': + aBuffer.append( sal_Unicode( '.' ) ); + break; + case '*': + aBuffer.append( sal_Unicode( '.' ) ).append( sal_Unicode( '*' ) ); + break; + case '\\': case '.': case '|': case '(': case ')': case '^': case '$': + // quote RE meta characters + aBuffer.append( sal_Unicode( '\\' ) ).append( *pcChar ); + break; + default: + aBuffer.append( *pcChar ); + } + } + rValue = aBuffer.makeStringAndClear(); + return true; + } + return false; +} + +} // namespace + +// ============================================================================ + +ApiFilterSettings::ApiFilterSettings() +{ +} + +void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, double fValue ) +{ + maFilterFields.resize( maFilterFields.size() + 1 ); + TableFilterField2& rFilterField = maFilterFields.back(); + rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR; + rFilterField.Operator = nOperator; + rFilterField.IsNumeric = sal_True; + rFilterField.NumericValue = fValue; +} + +void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, const OUString& rValue ) +{ + maFilterFields.resize( maFilterFields.size() + 1 ); + TableFilterField2& rFilterField = maFilterFields.back(); + rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR; + rFilterField.Operator = nOperator; + rFilterField.IsNumeric = sal_False; + rFilterField.StringValue = rValue; +} + +// ============================================================================ + +FilterSettingsBase::FilterSettingsBase( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +void FilterSettingsBase::importAttribs( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ ) +{ +} + +void FilterSettingsBase::importRecord( sal_Int32 /*nRecId*/, RecordInputStream& /*rStrm*/ ) +{ +} + +void FilterSettingsBase::importBiffRecord( BiffInputStream& /*rStrm*/, sal_uInt16 /*nFlags*/ ) +{ +} + +ApiFilterSettings FilterSettingsBase::finalizeImport( sal_Int32 /*nMaxCount*/ ) +{ + return ApiFilterSettings(); +} + +// ============================================================================ + +DiscreteFilter::DiscreteFilter( const WorkbookHelper& rHelper ) : + FilterSettingsBase( rHelper ), + mnCalendarType( XML_none ), + mbShowBlank( false ) +{ +} + +void DiscreteFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + switch( nElement ) + { + case XLS_TOKEN( filters ): + mnCalendarType = rAttribs.getToken( XML_calendarType, XML_none ); + mbShowBlank = rAttribs.getBool( XML_blank, false ); + break; + + case XLS_TOKEN( filter ): + { + OUString aValue = rAttribs.getXString( XML_val, OUString() ); + if( aValue.getLength() > 0 ) + maValues.push_back( aValue ); + } + break; + } +} + +void DiscreteFilter::importRecord( sal_Int32 nRecId, RecordInputStream& rStrm ) +{ + switch( nRecId ) + { + case BIFF12_ID_DISCRETEFILTERS: + { + sal_Int32 nShowBlank, nCalendarType; + rStrm >> nShowBlank >> nCalendarType; + + static const sal_Int32 spnCalendarTypes[] = { + XML_none, XML_gregorian, XML_gregorianUs, XML_japan, XML_taiwan, XML_korea, XML_hijri, XML_thai, XML_hebrew, + XML_gregorianMeFrench, XML_gregorianArabic, XML_gregorianXlitEnglish, XML_gregorianXlitFrench }; + mnCalendarType = STATIC_ARRAY_SELECT( spnCalendarTypes, nCalendarType, XML_none ); + mbShowBlank = nShowBlank != 0; + } + break; + + case BIFF12_ID_DISCRETEFILTER: + { + OUString aValue = rStrm.readString(); + if( aValue.getLength() > 0 ) + maValues.push_back( aValue ); + } + break; + } +} + +ApiFilterSettings DiscreteFilter::finalizeImport( sal_Int32 nMaxCount ) +{ + ApiFilterSettings aSettings; + if( static_cast< sal_Int32 >( maValues.size() ) <= nMaxCount ) + { + aSettings.maFilterFields.reserve( maValues.size() ); + + // insert all filter values + for( FilterValueVector::iterator aIt = maValues.begin(), aEnd = maValues.end(); aIt != aEnd; ++aIt ) + aSettings.appendField( false, FilterOperator2::EQUAL, *aIt ); + + // extra field for 'show empty' + if( mbShowBlank ) + aSettings.appendField( false, FilterOperator2::EMPTY, OUString() ); + + /* Require disabled regular expressions, filter entries may contain + any RE meta characters. */ + if( !maValues.empty() ) + aSettings.mobNeedsRegExp = false; + } + return aSettings; +} + +// ============================================================================ + +Top10Filter::Top10Filter( const WorkbookHelper& rHelper ) : + FilterSettingsBase( rHelper ), + mfValue( 0.0 ), + mbTop( true ), + mbPercent( false ) +{ +} + +void Top10Filter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + if( nElement == XLS_TOKEN( top10 ) ) + { + mfValue = rAttribs.getDouble( XML_val, 0.0 ); + mbTop = rAttribs.getBool( XML_top, true ); + mbPercent = rAttribs.getBool( XML_percent, false ); + } +} + +void Top10Filter::importRecord( sal_Int32 nRecId, RecordInputStream& rStrm ) +{ + if( nRecId == BIFF12_ID_TOP10FILTER ) + { + sal_uInt8 nFlags; + rStrm >> nFlags >> mfValue; + mbTop = getFlag( nFlags, BIFF12_TOP10FILTER_TOP ); + mbPercent = getFlag( nFlags, BIFF12_TOP10FILTER_PERCENT ); + } +} + +void Top10Filter::importBiffRecord( BiffInputStream& /*rStrm*/, sal_uInt16 nFlags ) +{ + mfValue = extractValue< sal_uInt16 >( nFlags, 7, 9 ); + mbTop = getFlag( nFlags, BIFF_FILTERCOLUMN_TOP ); + mbPercent = getFlag( nFlags, BIFF_FILTERCOLUMN_PERCENT ); +} + +ApiFilterSettings Top10Filter::finalizeImport( sal_Int32 /*nMaxCount*/ ) +{ + sal_Int32 nOperator = mbTop ? + (mbPercent ? FilterOperator2::TOP_PERCENT : FilterOperator2::TOP_VALUES) : + (mbPercent ? FilterOperator2::BOTTOM_PERCENT : FilterOperator2::BOTTOM_VALUES); + ApiFilterSettings aSettings; + aSettings.appendField( true, nOperator, mfValue ); + return aSettings; +} + +// ============================================================================ + +FilterCriterionModel::FilterCriterionModel() : + mnOperator( XML_equal ), + mnDataType( BIFF_FILTER_DATATYPE_NONE ), + mnStrLen( 0 ) +{ +} + +void FilterCriterionModel::setBiffOperator( sal_uInt8 nOperator ) +{ + static const sal_Int32 spnOperators[] = { XML_TOKEN_INVALID, + XML_lessThan, XML_equal, XML_lessThanOrEqual, XML_greaterThan, XML_notEqual, XML_greaterThanOrEqual }; + mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID ); +} + +void FilterCriterionModel::readBiffData( RecordInputStream& rStrm ) +{ + sal_uInt8 nOperator; + rStrm >> mnDataType >> nOperator; + setBiffOperator( nOperator ); + + switch( mnDataType ) + { + case BIFF_FILTER_DATATYPE_DOUBLE: + maValue <<= rStrm.readDouble(); + break; + case BIFF_FILTER_DATATYPE_STRING: + { + rStrm.skip( 8 ); + OUString aValue = rStrm.readString().trim(); + if( aValue.getLength() > 0 ) + maValue <<= aValue; + } + break; + case BIFF_FILTER_DATATYPE_BOOLEAN: + maValue <<= (rStrm.readuInt8() != 0); + rStrm.skip( 7 ); + break; + case BIFF_FILTER_DATATYPE_EMPTY: + rStrm.skip( 8 ); + if( mnOperator == XML_equal ) + maValue <<= OUString(); + break; + case BIFF_FILTER_DATATYPE_NOTEMPTY: + rStrm.skip( 8 ); + if( mnOperator == XML_notEqual ) + maValue <<= OUString(); + break; + default: + OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" ); + rStrm.skip( 8 ); + } +} + +void FilterCriterionModel::readBiffData( BiffInputStream& rStrm ) +{ + sal_uInt8 nOperator; + rStrm >> mnDataType >> nOperator; + setBiffOperator( nOperator ); + + switch( mnDataType ) + { + case BIFF_FILTER_DATATYPE_NONE: + rStrm.skip( 8 ); + break; + case BIFF_FILTER_DATATYPE_RK: + maValue <<= BiffHelper::calcDoubleFromRk( rStrm.readInt32() ); + rStrm.skip( 4 ); + break; + case BIFF_FILTER_DATATYPE_DOUBLE: + maValue <<= rStrm.readDouble(); + break; + case BIFF_FILTER_DATATYPE_STRING: + rStrm.skip( 4 ); + rStrm >> mnStrLen; + rStrm.skip( 3 ); + break; + case BIFF_FILTER_DATATYPE_BOOLEAN: + { + sal_uInt8 nValue, nType; + rStrm >> nValue >> nType; + rStrm.skip( 6 ); + switch( nType ) + { + case BIFF_BOOLERR_BOOL: maValue <<= (nValue != 0); break; + case BIFF_BOOLERR_ERROR: maValue <<= BiffHelper::calcDoubleFromError( nValue ); break; + default: OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unknown type" ); + } + } + break; + case BIFF_FILTER_DATATYPE_EMPTY: + rStrm.skip( 8 ); + if( mnOperator == XML_equal ) + maValue <<= OUString(); + break; + case BIFF_FILTER_DATATYPE_NOTEMPTY: + rStrm.skip( 8 ); + if( mnOperator == XML_notEqual ) + maValue <<= OUString(); + break; + default: + OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" ); + rStrm.skip( 8 ); + } +} + +void FilterCriterionModel::readString( BiffInputStream& rStrm, BiffType eBiff, rtl_TextEncoding eTextEnc ) +{ + if( (mnDataType == BIFF_FILTER_DATATYPE_STRING) && (mnStrLen > 0) ) + { + OUString aValue = (eBiff == BIFF8) ? + rStrm.readUniStringBody( mnStrLen, true ) : + rStrm.readCharArrayUC( mnStrLen, eTextEnc, true ); + aValue = aValue.trim(); + if( aValue.getLength() > 0 ) + maValue <<= aValue; + } +} + +// ---------------------------------------------------------------------------- + +CustomFilter::CustomFilter( const WorkbookHelper& rHelper ) : + FilterSettingsBase( rHelper ), + mbAnd( false ) +{ +} + +void CustomFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + switch( nElement ) + { + case XLS_TOKEN( customFilters ): + mbAnd = rAttribs.getBool( XML_and, false ); + break; + + case XLS_TOKEN( customFilter ): + { + FilterCriterionModel aCriterion; + aCriterion.mnOperator = rAttribs.getToken( XML_operator, XML_equal ); + OUString aValue = rAttribs.getXString( XML_val, OUString() ).trim(); + if( (aCriterion.mnOperator == XML_equal) || (aCriterion.mnOperator == XML_notEqual) || (aValue.getLength() > 0) ) + aCriterion.maValue <<= aValue; + appendCriterion( aCriterion ); + } + break; + } +} + +void CustomFilter::importRecord( sal_Int32 nRecId, RecordInputStream& rStrm ) +{ + switch( nRecId ) + { + case BIFF12_ID_CUSTOMFILTERS: + mbAnd = rStrm.readInt32() == 0; + break; + + case BIFF12_ID_CUSTOMFILTER: + { + FilterCriterionModel aCriterion; + aCriterion.readBiffData( rStrm ); + appendCriterion( aCriterion ); + } + break; + } +} + +void CustomFilter::importBiffRecord( BiffInputStream& rStrm, sal_uInt16 nFlags ) +{ + mbAnd = !getFlag( nFlags, BIFF_FILTERCOLUMN_OR ); + + FilterCriterionModel aCriterion1, aCriterion2; + aCriterion1.readBiffData( rStrm ); + aCriterion2.readBiffData( rStrm ); + aCriterion1.readString( rStrm, getBiff(), getTextEncoding() ); + aCriterion2.readString( rStrm, getBiff(), getTextEncoding() ); + appendCriterion( aCriterion1 ); + appendCriterion( aCriterion2 ); +} + +ApiFilterSettings CustomFilter::finalizeImport( sal_Int32 /*nMaxCount*/ ) +{ + ApiFilterSettings aSettings; + OSL_ENSURE( maCriteria.size() <= 2, "CustomFilter::finalizeImport - too many filter criteria" ); + for( FilterCriterionVector::iterator aIt = maCriteria.begin(), aEnd = maCriteria.end(); aIt != aEnd; ++aIt ) + { + // first extract the filter operator + sal_Int32 nOperator = 0; + bool bValidOperator = lclGetApiOperatorFromToken( nOperator, aIt->mnOperator ); + if( bValidOperator ) + { + if( aIt->maValue.has< OUString >() ) + { + // string argument + OUString aValue; + aIt->maValue >>= aValue; + // check for 'empty', 'contains', 'begins with', or 'ends with' text filters + bool bEqual = nOperator == FilterOperator2::EQUAL; + bool bNotEqual = nOperator == FilterOperator2::NOT_EQUAL; + if( bEqual || bNotEqual ) + { + if( aValue.getLength() == 0 ) + { + // empty comparison string: create empty/not empty filters + nOperator = bNotEqual ? FilterOperator2::NOT_EMPTY : FilterOperator2::EMPTY; + } + else + { + // compare to something: try to find begins/ends/contains + bool bHasLeadingAsterisk = lclTrimLeadingAsterisks( aValue ); + bool bHasTrailingAsterisk = lclTrimTrailingAsterisks( aValue ); + // just '***' matches everything, do not create a filter field + bValidOperator = aValue.getLength() > 0; + if( bValidOperator ) + { + if( bHasLeadingAsterisk && bHasTrailingAsterisk ) + nOperator = bNotEqual ? FilterOperator2::DOES_NOT_CONTAIN : FilterOperator2::CONTAINS; + else if( bHasLeadingAsterisk ) + nOperator = bNotEqual ? FilterOperator2::DOES_NOT_END_WITH : FilterOperator2::ENDS_WITH; + else if( bHasTrailingAsterisk ) + nOperator = bNotEqual ? FilterOperator2::DOES_NOT_BEGIN_WITH : FilterOperator2::BEGINS_WITH; + // else: no asterisks, stick to equal/not equal + } + } + } + + if( bValidOperator ) + { + // if wildcards are present, require RE mode, otherwise keep don't care state + if( lclConvertWildcardsToRegExp( aValue ) ) + aSettings.mobNeedsRegExp = true; + // create a new UNO API filter field + aSettings.appendField( mbAnd, nOperator, aValue ); + } + } + else if( aIt->maValue.has< double >() ) + { + // floating-point argument + double fValue; + aIt->maValue >>= fValue; + aSettings.appendField( mbAnd, nOperator, fValue ); + } + } + } + return aSettings; +} + +void CustomFilter::appendCriterion( const FilterCriterionModel& rCriterion ) +{ + if( (rCriterion.mnOperator != XML_TOKEN_INVALID) && rCriterion.maValue.hasValue() ) + maCriteria.push_back( rCriterion ); +} + +// ============================================================================ + +FilterColumn::FilterColumn( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mnColId( -1 ), + mbHiddenButton( false ), + mbShowButton( true ) +{ +} + +void FilterColumn::importFilterColumn( const AttributeList& rAttribs ) +{ + mnColId = rAttribs.getInteger( XML_colId, -1 ); + mbHiddenButton = rAttribs.getBool( XML_hiddenButton, false ); + mbShowButton = rAttribs.getBool( XML_showButton, true ); +} + +void FilterColumn::importFilterColumn( RecordInputStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> mnColId >> nFlags; + mbHiddenButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_HIDDENBUTTON ); + mbShowButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_SHOWBUTTON ); +} + +void FilterColumn::importFilterColumn( BiffInputStream& rStrm ) +{ + sal_uInt16 nFlags; + mnColId = rStrm.readuInt16(); + rStrm >> nFlags; + + // BIFF5/BIFF8 support top-10 filters and custom filters + if( getFlag( nFlags, BIFF_FILTERCOLUMN_TOP10FILTER ) ) + createFilterSettings< Top10Filter >().importBiffRecord( rStrm, nFlags ); + else + createFilterSettings< CustomFilter >().importBiffRecord( rStrm, nFlags ); +} + +ApiFilterSettings FilterColumn::finalizeImport( sal_Int32 nMaxCount ) +{ + ApiFilterSettings aSettings; + if( (0 <= mnColId) && mxSettings.get() ) + { + // filter settings object creates a sequence of filter fields + aSettings = mxSettings->finalizeImport( nMaxCount ); + // add column index to all filter fields + for( ApiFilterSettings::FilterFieldVector::iterator aIt = aSettings.maFilterFields.begin(), aEnd = aSettings.maFilterFields.end(); aIt != aEnd; ++aIt ) + aIt->Field = mnColId; + } + return aSettings; +} + +// ============================================================================ + +AutoFilter::AutoFilter( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +void AutoFilter::importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet ) +{ + OUString aRangeStr = rAttribs.getString( XML_ref, OUString() ); + getAddressConverter().convertToCellRangeUnchecked( maRange, aRangeStr, nSheet ); +} + +void AutoFilter::importAutoFilter( RecordInputStream& rStrm, sal_Int16 nSheet ) +{ + BinRange aBinRange; + rStrm >> aBinRange; + getAddressConverter().convertToCellRangeUnchecked( maRange, aBinRange, nSheet ); +} + +FilterColumn& AutoFilter::createFilterColumn() +{ + FilterColumnVector::value_type xFilterColumn( new FilterColumn( *this ) ); + maFilterColumns.push_back( xFilterColumn ); + return *xFilterColumn; +} + +void AutoFilter::finalizeImport( const Reference< XSheetFilterDescriptor2 >& rxFilterDesc ) +{ + if( rxFilterDesc.is() ) + { + // set some common properties for the auto filter range + PropertySet aDescProps( rxFilterDesc ); + aDescProps.setProperty( PROP_IsCaseSensitive, false ); + aDescProps.setProperty( PROP_SkipDuplicates, false ); + aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS ); + aDescProps.setProperty( PROP_ContainsHeader, true ); + aDescProps.setProperty( PROP_CopyOutputData, false ); + + // maximum number of UNO API filter fields + sal_Int32 nMaxCount = 0; + aDescProps.getProperty( nMaxCount, PROP_MaxFieldCount ); + OSL_ENSURE( nMaxCount > 0, "AutoFilter::finalizeImport - invalid maximum filter field count" ); + + // resulting list of all UNO API filter fields + ::std::vector< TableFilterField2 > aFilterFields; + + // track if columns require to enable or disable regular expressions + OptValue< bool > obNeedsRegExp; + + /* Track whether the filter fields of the first filter column are + connected with 'or'. In this case, other filter fields cannot be + inserted without altering the result of the entire filter, due to + Calc's precedence for the 'and' connection operator. Example: + Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and + B2 belong to filter column B, will be evaluated by Calc as + '(A1 and B1) or (B2 and C1)'. */ + bool bHasOrConnection = false; + + // process all filter column objects, exit when 'or' connection exists + for( FilterColumnVector::iterator aIt = maFilterColumns.begin(), aEnd = maFilterColumns.end(); !bHasOrConnection && (aIt != aEnd); ++aIt ) + { + // the filter settings object creates a list of filter fields + ApiFilterSettings aSettings = (*aIt)->finalizeImport( nMaxCount ); + ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields; + + // new total number of filter fields + sal_Int32 nNewCount = static_cast< sal_Int32 >( aFilterFields.size() + rColumnFields.size() ); + + /* Check whether mode for regular expressions is compatible with + the global mode in obNeedsRegExp. If either one is still in + don't-care state, all is fine. If both are set, they must be + equal. */ + bool bRegExpCompatible = !obNeedsRegExp || !aSettings.mobNeedsRegExp || (obNeedsRegExp.get() == aSettings.mobNeedsRegExp.get()); + + // check whether fields are connected by 'or' (see comments above). + if( rColumnFields.size() >= 2 ) + for( ApiFilterSettings::FilterFieldVector::iterator aSIt = rColumnFields.begin() + 1, aSEnd = rColumnFields.end(); !bHasOrConnection && (aSIt != aSEnd); ++aSIt ) + bHasOrConnection = aSIt->Connection == FilterConnection_OR; + + /* Skip the column filter, if no filter fields have been created, + if the number of new filter fields would exceed the total limit + of filter fields, or if the mode for regular expressions of the + filter column does not fit. */ + if( !rColumnFields.empty() && (nNewCount <= nMaxCount) && bRegExpCompatible ) + { + /* Add 'and' connection to the first filter field to connect + it to the existing filter fields of other columns. */ + rColumnFields[ 0 ].Connection = FilterConnection_AND; + + // insert the new filter fields + aFilterFields.insert( aFilterFields.end(), rColumnFields.begin(), rColumnFields.end() ); + + // update the regular expressions mode + obNeedsRegExp.assignIfUsed( aSettings.mobNeedsRegExp ); + } + } + + // insert all filter fields to the filter descriptor + if( !aFilterFields.empty() ) + rxFilterDesc->setFilterFields2( ContainerHelper::vectorToSequence( aFilterFields ) ); + + // regular expressions + bool bUseRegExp = obNeedsRegExp.get( false ); + aDescProps.setProperty( PROP_UseRegularExpressions, bUseRegExp ); + } +} + +// ============================================================================ + +AutoFilterBuffer::AutoFilterBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +AutoFilter& AutoFilterBuffer::createAutoFilter() +{ + AutoFilterVector::value_type xAutoFilter( new AutoFilter( *this ) ); + maAutoFilters.push_back( xAutoFilter ); + return *xAutoFilter; +} + +void AutoFilterBuffer::finalizeImport( sal_Int16 nSheet ) +{ + // rely on existence of the defined name '_FilterDatabase' containing the range address of the filtered area + if( const DefinedName* pFilterDBName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_FILTERDATABASE, nSheet ).get() ) + { + CellRangeAddress aFilterRange; + if( pFilterDBName->getAbsoluteRange( aFilterRange ) && (aFilterRange.Sheet == nSheet) ) + { + // use the same name for the database range as used for the defined name '_FilterDatabase' + OUString aDBRangeName = pFilterDBName->getCalcName(); + Reference< XDatabaseRange > xDatabaseRange = createDatabaseRangeObject( aDBRangeName, aFilterRange ); + // first, try to create an auto filter + bool bHasAutoFilter = finalizeImport( xDatabaseRange ); + // no success: try to create an advanced filter + if( !bHasAutoFilter && xDatabaseRange.is() ) + { + // the built-in defined name 'Criteria' must exist + if( const DefinedName* pCriteriaName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_CRITERIA, nSheet ).get() ) + { + CellRangeAddress aCriteriaRange; + if( pCriteriaName->getAbsoluteRange( aCriteriaRange ) ) + { + // set some common properties for the filter descriptor + PropertySet aDescProps( xDatabaseRange->getFilterDescriptor() ); + aDescProps.setProperty( PROP_IsCaseSensitive, false ); + aDescProps.setProperty( PROP_SkipDuplicates, false ); + aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS ); + aDescProps.setProperty( PROP_ContainsHeader, true ); + // criteria range may contain wildcards, but these are incompatible with REs + aDescProps.setProperty( PROP_UseRegularExpressions, false ); + + // position of output data (if built-in defined name 'Extract' exists) + DefinedNameRef xExtractName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_EXTRACT, nSheet ); + CellRangeAddress aOutputRange; + bool bHasOutputRange = xExtractName.get() && xExtractName->getAbsoluteRange( aOutputRange ); + aDescProps.setProperty( PROP_CopyOutputData, bHasOutputRange ); + if( bHasOutputRange ) + { + aDescProps.setProperty( PROP_SaveOutputPosition, true ); + aDescProps.setProperty( PROP_OutputPosition, CellAddress( aOutputRange.Sheet, aOutputRange.StartColumn, aOutputRange.StartRow ) ); + } + + /* Properties of the database range (must be set after + modifying properties of the filter descriptor, + otherwise the 'FilterCriteriaSource' property gets + deleted). */ + PropertySet aRangeProps( xDatabaseRange ); + aRangeProps.setProperty( PROP_AutoFilter, false ); + aRangeProps.setProperty( PROP_FilterCriteriaSource, aCriteriaRange ); + } + } + } + } + } +} + +bool AutoFilterBuffer::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRange ) +{ + AutoFilter* pAutoFilter = getActiveAutoFilter(); + if( pAutoFilter && rxDatabaseRange.is() ) try + { + // the property 'AutoFilter' enables the drop-down buttons + PropertySet aRangeProps( rxDatabaseRange ); + aRangeProps.setProperty( PROP_AutoFilter, true ); + // convert filter settings using the filter descriptor of the database range + Reference< XSheetFilterDescriptor2 > xFilterDesc( rxDatabaseRange->getFilterDescriptor(), UNO_QUERY_THROW ); + pAutoFilter->finalizeImport( xFilterDesc ); + // return true to indicate enabled autofilter + return true; + } + catch( Exception& ) + { + } + return false; +} + +AutoFilter* AutoFilterBuffer::getActiveAutoFilter() +{ + // Excel expects not more than one auto filter per sheet or table + OSL_ENSURE( maAutoFilters.size() == 1, "AutoFilterBuffer::getActiveAutoFilter - too many auto filters" ); + // stick to the last imported auto filter + return maAutoFilters.empty() ? 0 : maAutoFilters.back().get(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox diff --git a/oox/source/xls/autofiltercontext.cxx b/oox/source/xls/autofiltercontext.cxx index 87514d7804c5..d88e7026b80f 100644 --- a/oox/source/xls/autofiltercontext.cxx +++ b/oox/source/xls/autofiltercontext.cxx @@ -27,745 +27,156 @@ #include "oox/xls/autofiltercontext.hxx" -#include <com/sun/star/container/XNameAccess.hpp> -#include <com/sun/star/container/XNamed.hpp> -#include <com/sun/star/i18n/XLocaleData.hpp> -#include <com/sun/star/sheet/FilterConnection.hpp> -#include <com/sun/star/sheet/FilterOperator.hpp> -#include <com/sun/star/sheet/XDatabaseRange.hpp> -#include <com/sun/star/sheet/XDatabaseRanges.hpp> -#include <com/sun/star/sheet/XSheetFilterDescriptor.hpp> -#include <com/sun/star/table/XCellRange.hpp> -#include <rtl/ustrbuf.hxx> -#include "oox/core/filterbase.hxx" -#include "oox/helper/attributelist.hxx" -#include "oox/helper/propertyset.hxx" -#include "oox/xls/addressconverter.hxx" -#include "properties.hxx" - -#define DEBUG_OOX_AUTOFILTER 0 - -#if USE_SC_MULTI_STRING_FILTER_PATCH -#include <com/sun/star/sheet/XExtendedSheetFilterDescriptor.hpp> -#include <com/sun/star/sheet/TableFilterFieldNormal.hpp> -#include <com/sun/star/sheet/TableFilterFieldMultiString.hpp> -using ::com::sun::star::sheet::TableFilterFieldNormal; -using ::com::sun::star::sheet::TableFilterFieldMultiString; -using ::com::sun::star::sheet::XExtendedSheetFilterDescriptor; -#else -#include <com/sun/star/sheet/TableFilterField.hpp> -using ::com::sun::star::sheet::TableFilterField; -#endif - -#if DEBUG_OOX_AUTOFILTER -#include <stdio.h> -#endif - -using ::rtl::OUString; -using ::rtl::OUStringBuffer; -using ::com::sun::star::uno::Reference; -using ::com::sun::star::uno::Exception; -using ::com::sun::star::uno::Sequence; -using ::com::sun::star::uno::UNO_QUERY; -using ::com::sun::star::uno::UNO_QUERY_THROW; -using ::com::sun::star::container::XNameAccess; -using ::com::sun::star::container::XNamed; -using ::com::sun::star::table::CellRangeAddress; -using ::com::sun::star::table::XCellRange; -using ::com::sun::star::sheet::XDatabaseRange; -using ::com::sun::star::sheet::XDatabaseRanges; -using ::com::sun::star::sheet::XSheetFilterDescriptor; -using ::com::sun::star::i18n::LocaleDataItem; -using ::com::sun::star::i18n::XLocaleData; -using ::com::sun::star::lang::Locale; -using ::oox::core::ContextHandlerRef; +#include "oox/xls/autofilterbuffer.hxx" +#include "oox/xls/biffinputstream.hxx" namespace oox { namespace xls { -// ============================================================================ - -FilterFieldItem::FilterFieldItem() : -#if USE_SC_MULTI_STRING_FILTER_PATCH - mpField(new TableFilterFieldNormal), -#else - mpField(new TableFilterField), -#endif - meType(NORMAL) -{ -} - -FilterFieldItem::FilterFieldItem(Type eType) : - meType(eType) -{ -#if USE_SC_MULTI_STRING_FILTER_PATCH - switch ( eType ) - { - case MULTI_STRING: - mpField.reset(new TableFilterFieldMultiString); - break; - case NORMAL: - mpField.reset(new TableFilterFieldNormal); - break; - default: - mpField.reset(new TableFilterFieldNormal); - } -#else - mpField.reset(new TableFilterField); - meType = NORMAL; -#endif -} +using ::oox::core::ContextHandlerRef; +using ::rtl::OUString; // ============================================================================ -AutoFilterContext::AutoFilterContext( WorksheetFragmentBase& rFragment ) : - WorksheetContextBase( rFragment ), - mbValidAddress( false ), - mbUseRegex( false ), - mbShowBlank( false ), - mbConnectionAnd( false ) +FilterSettingsContext::FilterSettingsContext( WorksheetContextBase& rParent, FilterSettingsBase& rFilterSettings ) : + WorksheetContextBase( rParent ), + mrFilterSettings( rFilterSettings ) { } -ContextHandlerRef AutoFilterContext::onCreateContext( sal_Int32 nElement, const AttributeList& ) +ContextHandlerRef FilterSettingsContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) { switch( getCurrentElement() ) { - case XLS_TOKEN( autoFilter ): - switch( nElement ) - { - case XLS_TOKEN( filterColumn ): return this; - } - break; - - case XLS_TOKEN( filterColumn ): - switch( nElement ) - { - case XLS_TOKEN( filters ): - case XLS_TOKEN( customFilters ): - case XLS_TOKEN( top10 ): - case XLS_TOKEN( dynamicFilter ): return this; - } - break; - case XLS_TOKEN( filters ): - switch( nElement ) - { - case XLS_TOKEN( filter ): return this; - } + if( nElement == XLS_TOKEN( filter ) ) return this; break; - case XLS_TOKEN( customFilters ): - switch( nElement ) - { - case XLS_TOKEN( customFilter ): return this; - } + if( nElement == XLS_TOKEN( customFilter ) ) return this; break; } return 0; } -void AutoFilterContext::onStartElement( const AttributeList& rAttribs ) +void FilterSettingsContext::onStartElement( const AttributeList& rAttribs ) { - switch( getCurrentElement() ) - { - case XLS_TOKEN( autoFilter ): - importAutoFilter( rAttribs ); - break; - case XLS_TOKEN( filterColumn ): - if ( mbValidAddress ) - importFilterColumn( rAttribs ); - break; - case XLS_TOKEN( filters ): - if ( mbValidAddress ) - importFilters( rAttribs ); - break; - case XLS_TOKEN( filter ): - if ( mbValidAddress ) - importFilter( rAttribs ); - break; - case XLS_TOKEN( customFilters ): - if ( mbValidAddress ) - importCustomFilters( rAttribs ); - break; - case XLS_TOKEN( customFilter ): - if ( mbValidAddress ) - importCustomFilter( rAttribs ); - break; - case XLS_TOKEN( top10 ): - if ( mbValidAddress ) - importTop10( rAttribs ); - break; - case XLS_TOKEN( dynamicFilter ): - if ( mbValidAddress ) - importDynamicFilter( rAttribs ); - break; - } + mrFilterSettings.importAttribs( getCurrentElement(), rAttribs ); } -void AutoFilterContext::onEndElement() +ContextHandlerRef FilterSettingsContext::onCreateRecordContext( sal_Int32 nRecId, RecordInputStream& /*rStrm*/ ) { switch( getCurrentElement() ) { - case XLS_TOKEN( autoFilter ): - maybeShowBlank(); - setAutoFilter(); + case BIFF12_ID_DISCRETEFILTERS: + if( nRecId == BIFF12_ID_DISCRETEFILTER ) return this; break; - case XLS_TOKEN( filters ): - setFilterNames(); + case BIFF12_ID_CUSTOMFILTERS: + if( nRecId == BIFF12_ID_CUSTOMFILTER ) return this; break; } + return 0; } -#if DEBUG_OOX_AUTOFILTER -static void lclPrintNormalField( -#if USE_SC_MULTI_STRING_FILTER_PATCH - TableFilterFieldNormal* pField -#else - TableFilterField* pField -#endif -) +void FilterSettingsContext::onStartRecord( RecordInputStream& rStrm ) { - using namespace ::com::sun::star::sheet; - - printf(" Operator: "); - switch ( pField->Operator ) - { - case FilterOperator_EQUAL: - printf("EQUAL"); - break; - case FilterOperator_NOT_EQUAL: - printf("NOT_EQUAL"); - break; - case com::sun::star::sheet::FilterOperator_GREATER: - printf("GREATER"); - break; - case com::sun::star::sheet::FilterOperator_GREATER_EQUAL: - printf("GREATER_EQUAL"); - break; - case FilterOperator_LESS: - printf("LESS"); - break; - case FilterOperator_LESS_EQUAL: - printf("LESS_EQUAL"); - break; - case FilterOperator_NOT_EMPTY: - printf("NOT_EMPTY"); - break; - case FilterOperator_EMPTY: - printf("EMPTY"); - break; - case FilterOperator_BOTTOM_PERCENT: - printf("BOTTOM_PERCENT"); - break; - case FilterOperator_BOTTOM_VALUES: - printf("BOTTOM_VALUES"); - break; - case FilterOperator_TOP_PERCENT: - printf("TOP_PERCENT"); - break; - case FilterOperator_TOP_VALUES: - printf("TOP_VALUES"); - break; - default: - printf("other"); - } - printf("\n"); - - printf(" StringValue: %s\n", - OUStringToOString(pField->StringValue, RTL_TEXTENCODING_UTF8).getStr()); - - printf(" NumericValue: %g\n", pField->NumericValue); - - printf(" IsNumeric: "); - if (pField->IsNumeric) - printf("yes\n"); - else - printf("no\n"); + mrFilterSettings.importRecord( getCurrentElement(), rStrm ); } -static void lclPrintFieldConnection( ::com::sun::star::sheet::FilterConnection eConn ) -{ - using namespace ::com::sun::star::sheet; +// ============================================================================ - printf(" Connection: "); - switch ( eConn ) - { - case FilterConnection_AND: - printf("AND"); - break; - case FilterConnection_OR: - printf("OR"); - break; - case FilterConnection_MAKE_FIXED_SIZE: - printf("MAKE_FIXED_SIZE"); - break; - default: - printf("other"); - } - printf("\n"); +FilterColumnContext::FilterColumnContext( WorksheetContextBase& rParent, FilterColumn& rFilterColumn ) : + WorksheetContextBase( rParent ), + mrFilterColumn( rFilterColumn ) +{ } -static void lclPrintFilterField( const FilterFieldItem& aItem ) +ContextHandlerRef FilterColumnContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) { - using namespace ::com::sun::star::sheet; - - printf("----------------------------------------\n"); -#if USE_SC_MULTI_STRING_FILTER_PATCH - { - // Print common fields first. - - TableFilterFieldBase* pField = aItem.mpField.get(); - printf(" Field: %ld\n", pField->Field); - lclPrintFieldConnection(pField->Connection); - } - switch ( aItem.meType ) + if( getCurrentElement() == XLS_TOKEN( filterColumn ) ) switch( nElement ) { - case FilterFieldItem::NORMAL: - { - TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>(aItem.mpField.get()); - lclPrintNormalField(pField); - } - break; - case FilterFieldItem::MULTI_STRING: - { - TableFilterFieldMultiString* pMultiStrField = static_cast<TableFilterFieldMultiString*>(aItem.mpField.get()); - sal_Int32 nSize = pMultiStrField->StringSet.getLength(); - printf(" StringSet:\n"); - for ( sal_Int32 i = 0; i < nSize; ++i ) - { - printf(" * %s\n", - OUStringToOString(pMultiStrField->StringSet[i], RTL_TEXTENCODING_UTF8).getStr()); - } - } - break; + case XLS_TOKEN( filters ): + return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< DiscreteFilter >() ); + case XLS_TOKEN( top10 ): + return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< Top10Filter >() ); + case XLS_TOKEN( customFilters ): + return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< CustomFilter >() ); } -#else - TableFilterField* pField = aItem.mpField.get(); - printf(" Field: %ld\n", pField->Field); - lclPrintFieldConnection(pField->Connection); - lclPrintNormalField(pField); - -#endif - fflush(stdout); + return 0; } -#endif -void AutoFilterContext::initialize() +void FilterColumnContext::onStartElement( const AttributeList& rAttribs ) { - maFields.clear(); - maFilterNames.clear(); - mbValidAddress = mbShowBlank = mbUseRegex = mbConnectionAnd = false; + mrFilterColumn.importFilterColumn( rAttribs ); } -void AutoFilterContext::setAutoFilter() +ContextHandlerRef FilterColumnContext::onCreateRecordContext( sal_Int32 nRecId, RecordInputStream& /*rStrm*/ ) { - using namespace ::com::sun::star::sheet; - - // Name this built-in database. - OUStringBuffer sDataAreaNameBuf( CREATE_OUSTRING("Excel_BuiltIn__FilterDatabase_ ") ); - sDataAreaNameBuf.append( static_cast<sal_Int32>(getSheetIndex()+1) ); - - OUString sDataAreaName = sDataAreaNameBuf.makeStringAndClear(); - Reference< XCellRange > xCellRange = getCellRange( maAutoFilterRange ); - - // Create a new database range, add filters to it and refresh the database - // for that to take effect. - - PropertySet aDocProps( getDocument() ); - Reference< XDatabaseRanges > xDBRanges( aDocProps.getAnyProperty( PROP_DatabaseRanges ), UNO_QUERY ); - OSL_ENSURE( xDBRanges.is(), "AutoFilterContext::setAutoFilter: DBRange empty" ); - if ( !xDBRanges.is() ) - return; - - Reference< XNameAccess > xNA( xDBRanges, UNO_QUERY_THROW ); - if ( !xNA->hasByName( sDataAreaName ) ) - xDBRanges->addNewByName( sDataAreaName, maAutoFilterRange ); - - Reference< XDatabaseRange > xDB( xNA->getByName( sDataAreaName ), UNO_QUERY ); - if ( xDB.is() ) - { - PropertySet aProp( xDB ); - aProp.setProperty( PROP_AutoFilter, true ); - } - - sal_Int32 nSize = maFields.size(); - sal_Int32 nMaxFieldCount = nSize; - Reference< XSheetFilterDescriptor > xDescriptor = xDB->getFilterDescriptor(); - if ( xDescriptor.is() ) - { - PropertySet aProp( xDescriptor ); - aProp.setProperty( PROP_ContainsHeader, true ); - aProp.setProperty( PROP_UseRegularExpressions, mbUseRegex ); - aProp.getProperty( nMaxFieldCount, PROP_MaxFieldCount ); - } - else - { - OSL_ENSURE(false, "AutoFilterContext::setAutoFilter: descriptor is empty"); - return; - } - - // Unpack all column field items into a sequence. -#if USE_SC_MULTI_STRING_FILTER_PATCH - Reference< XExtendedSheetFilterDescriptor > xExtDescriptor( xDescriptor, UNO_QUERY ); - if ( !xExtDescriptor.is() ) - { - OSL_ENSURE(false, "AutoFilterContext::setAutoFilter: extended descriptor is empty"); - return; - } - - xExtDescriptor->begin(); - - ::std::list< FilterFieldItem >::const_iterator itr = maFields.begin(), itrEnd = maFields.end(); - for (sal_Int32 i = 0; itr != itrEnd && i < nMaxFieldCount; ++itr, ++i) + if( getCurrentElement() == BIFF12_ID_FILTERCOLUMN ) switch( nRecId ) { -#if DEBUG_OOX_AUTOFILTER - lclPrintFilterField(*itr); -#endif - switch ( itr->meType ) - { - case oox::xls::FilterFieldItem::MULTI_STRING: - { - // multi-string filter type - TableFilterFieldMultiString* pField = static_cast<TableFilterFieldMultiString*>( itr->mpField.get() ); - xExtDescriptor->addFilterFieldMultiString( *pField ); - } - break; - case oox::xls::FilterFieldItem::NORMAL: - default: - // normal filter type - TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>( itr->mpField.get() ); - xExtDescriptor->addFilterFieldNormal( *pField ); - } + case BIFF12_ID_DISCRETEFILTERS: + return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< DiscreteFilter >() ); + case BIFF12_ID_TOP10FILTER: + return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< Top10Filter >() ); + case BIFF12_ID_CUSTOMFILTERS: + return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< CustomFilter >() ); } - xExtDescriptor->commit(); - -#else - Sequence< TableFilterField > aFields(nSize); - ::std::list< FilterFieldItem >::const_iterator itr = maFields.begin(), itrEnd = maFields.end(); - for (sal_Int32 i = 0; itr != itrEnd && i < nMaxFieldCount; ++itr, ++i) - { -#if DEBUG_OOX_AUTOFILTER - lclPrintFilterField( *itr ); -#endif - aFields[i] = *itr->mpField; - } - xDescriptor->setFilterFields( aFields ); -#endif - xDB->refresh(); + return 0; } -void AutoFilterContext::maybeShowBlank() +void FilterColumnContext::onStartRecord( RecordInputStream& rStrm ) { - using namespace ::com::sun::star::sheet; - - if ( !mbShowBlank ) - return; - -#if USE_SC_MULTI_STRING_FILTER_PATCH - FilterFieldItem aItem(FilterFieldItem::NORMAL); - TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>(aItem.mpField.get()); - pField->Field = mnCurColID; - pField->Operator = FilterOperator_EMPTY; - pField->Connection = FilterConnection_AND; - pField->IsNumeric = false; -#else - FilterFieldItem aItem; - aItem.mpField->Field = mnCurColID; - aItem.mpField->Operator = FilterOperator_EMPTY; - aItem.mpField->Connection = FilterConnection_AND; - aItem.mpField->IsNumeric = false; -#endif - maFields.push_back(aItem); + mrFilterColumn.importFilterColumn( rStrm ); } -void AutoFilterContext::setFilterNames() -{ - using namespace ::com::sun::star::sheet; - - - sal_Int32 size = maFilterNames.size(); - if ( !size ) - return; - -#if USE_SC_MULTI_STRING_FILTER_PATCH - Sequence< OUString > aStrList(size); - ::std::list< OUString >::const_iterator itr = maFilterNames.begin(), itrEnd = maFilterNames.end(); - for (sal_Int32 i = 0; itr != itrEnd; ++itr, ++i) - aStrList[i] = *itr; - - FilterFieldItem aItem(FilterFieldItem::MULTI_STRING); - TableFilterFieldMultiString* pField = static_cast<TableFilterFieldMultiString*>( aItem.mpField.get() ); - pField->Field = mnCurColID; - pField->Connection = FilterConnection_AND; - pField->StringSet = aStrList; - - maFields.push_back(aItem); -#else - static const OUString sSep = CREATE_OUSTRING("|"); - - OUStringBuffer buf; - if ( size > 1 ) - { - buf.append( CREATE_OUSTRING("^(") ); - mbUseRegex = true; - } - - ::std::list< OUString >::const_iterator itr = maFilterNames.begin(), itrEnd = maFilterNames.end(); - bool bFirst = true; - for (; itr != itrEnd; ++itr) - { - if (bFirst) - bFirst = false; - else - buf.append( sSep ); - buf.append( *itr ); - } - if ( size > 1 ) - buf.append( CREATE_OUSTRING(")$") ); - - FilterFieldItem aItem; - aItem.mpField->Field = mnCurColID; - aItem.mpField->StringValue = buf.makeStringAndClear(); - aItem.mpField->Operator = FilterOperator_EQUAL; - aItem.mpField->Connection = FilterConnection_AND; - aItem.mpField->IsNumeric = false; - maFields.push_back(aItem); -#endif -} +// ============================================================================ -void AutoFilterContext::importAutoFilter( const AttributeList& rAttribs ) +AutoFilterContext::AutoFilterContext( WorksheetFragmentBase& rFragment, AutoFilter& rAutoFilter ) : + WorksheetContextBase( rFragment ), + mrAutoFilter( rAutoFilter ) { - initialize(); - - mbValidAddress = getAddressConverter().convertToCellRange( - maAutoFilterRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex(), true, true ); } -void AutoFilterContext::importFilterColumn( const AttributeList& rAttribs ) +ContextHandlerRef AutoFilterContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) { - // hiddenButton and showButton attributes are not used for now. - mnCurColID = rAttribs.getInteger( XML_colId, -1 ); + if( (getCurrentElement() == XLS_TOKEN( autoFilter )) && (nElement == XLS_TOKEN( filterColumn )) ) + return new FilterColumnContext( *this, mrAutoFilter.createFilterColumn() ); + return 0; } -void AutoFilterContext::importTop10( const AttributeList& rAttribs ) +void AutoFilterContext::onStartElement( const AttributeList& rAttribs ) { - using namespace ::com::sun::star::sheet; - - // filterVal attribute is not necessarily, since Calc also supports top 10 - // and top 10% filter type. - FilterFieldItem aItem; -#if USE_SC_MULTI_STRING_FILTER_PATCH - TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>(aItem.mpField.get()); -#else - TableFilterField* pField = aItem.mpField.get(); -#endif - pField->Field = mnCurColID; - - bool bPercent = rAttribs.getBool( XML_percent, false ); - bool bTop = rAttribs.getBool( XML_top, true ); - pField->NumericValue = rAttribs.getDouble( XML_val, 0.0 ); - pField->IsNumeric = true; - - // When top10 filter item is present, that's the only filter item for that column. - if ( bTop ) - if ( bPercent ) - pField->Operator = FilterOperator_TOP_PERCENT; - else - pField->Operator = FilterOperator_TOP_VALUES; - else - if ( bPercent ) - pField->Operator = FilterOperator_BOTTOM_PERCENT; - else - pField->Operator = FilterOperator_BOTTOM_VALUES; - - maFields.push_back(aItem); + mrAutoFilter.importAutoFilter( rAttribs, getSheetIndex() ); } -void AutoFilterContext::importCustomFilters( const AttributeList& rAttribs ) +ContextHandlerRef AutoFilterContext::onCreateRecordContext( sal_Int32 nRecId, RecordInputStream& /*rStrm*/ ) { - // OR is default when the 'and' attribute is absent. - mbConnectionAnd = rAttribs.getBool( XML_and, false ); + if( (getCurrentElement() == BIFF12_ID_AUTOFILTER) && (nRecId == BIFF12_ID_FILTERCOLUMN) ) + return new FilterColumnContext( *this, mrAutoFilter.createFilterColumn() ); + return 0; } -/** Do a best-effort guess of whether or not the given string is numerical. */ -static bool lclIsNumeric( const OUString& _str, const LocaleDataItem& aLocaleItem ) +void AutoFilterContext::onStartRecord( RecordInputStream& rStrm ) { - OUString str = _str.trim(); - sal_Int32 size = str.getLength(); - - if ( !size ) - // Empty string. This can't be a number. - return false; - - // Get the decimal separator for the current locale. - const OUString& sep = aLocaleItem.decimalSeparator; - - bool bDecimalSep = false; - for (sal_Int32 i = 0; i < size; ++i) - { - OUString c = str.copy(i, 1); - if ( !c.compareTo(sep) ) - { - if ( bDecimalSep ) - return false; - else - { - bDecimalSep = true; - continue; - } - } - if ( (0 > c.compareToAscii("0") || 0 < c.compareToAscii("9")) ) - return false; - } - - return true; + mrAutoFilter.importAutoFilter( rStrm, getSheetIndex() ); } -/** Convert wildcard characters to regex equivalent. Returns true if any - wildcard character is found. */ -static bool lclWildcard2Regex( OUString& str ) -{ - bool bWCFound = false; - OUStringBuffer buf; - sal_Int32 size = str.getLength(); - buf.ensureCapacity(size + 6); // pure heuristics. - - sal_Unicode dot = '.', star = '*', hat = '^', dollar = '$'; - buf.append(hat); - for (sal_Int32 i = 0; i < size; ++i) - { - OUString c = str.copy(i, 1); - if ( !c.compareToAscii("?") ) - { - buf.append(dot); - bWCFound = true; - } - else if ( !c.compareToAscii("*") ) - { - buf.append(dot); - buf.append(star); - bWCFound = true; - } - else - buf.append(c); - } - buf.append(dollar); - - if (bWCFound) - str = buf.makeStringAndClear(); - - return bWCFound; -} +// ============================================================================ -/** Translate Excel's filter operator to Calc's. */ -static ::com::sun::star::sheet::FilterOperator lclTranslateFilterOp( sal_Int32 nToken ) +BiffAutoFilterContext::BiffAutoFilterContext( const WorksheetHelper& rHelper, AutoFilter& rAutoFilter ) : + BiffWorksheetContextBase( rHelper ), + mrAutoFilter( rAutoFilter ) { - using namespace ::com::sun::star::sheet; - - switch ( nToken ) - { - case XML_equal: - return FilterOperator_EQUAL; - case XML_notEqual: - return FilterOperator_NOT_EQUAL; - case XML_greaterThan: - return FilterOperator_GREATER; - case XML_greaterThanOrEqual: - return FilterOperator_GREATER_EQUAL; - case XML_lessThan: - return FilterOperator_LESS; - case XML_lessThanOrEqual: - return FilterOperator_LESS_EQUAL; - } - return FilterOperator_EQUAL; } -void AutoFilterContext::importCustomFilter( const AttributeList& rAttribs ) +void BiffAutoFilterContext::importRecord( BiffInputStream& rStrm ) { - using namespace ::com::sun::star::sheet; - - sal_Int32 nToken = rAttribs.getToken( XML_operator, XML_equal ); -#if USE_SC_MULTI_STRING_FILTER_PATCH - FilterFieldItem aItem(FilterFieldItem::NORMAL); - TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>(aItem.mpField.get()); -#else - FilterFieldItem aItem; - TableFilterField* pField = aItem.mpField.get(); -#endif - pField->Field = mnCurColID; - pField->StringValue = rAttribs.getString( XML_val, OUString() ); - pField->NumericValue = pField->StringValue.toDouble(); - pField->Operator = lclTranslateFilterOp( nToken ); - - if ( nToken == XML_notEqual && !pField->StringValue.compareToAscii(" ") ) - { - // Special case for hiding blanks. Excel translates "hide blanks" to - // (filter if notEqual " "). So, we need to translate it back. - pField->Operator = FilterOperator_NOT_EMPTY; - pField->IsNumeric = false; - maFields.push_back(aItem); - return; - } - - switch ( nToken ) + switch( rStrm.getRecId() ) { - case XML_equal: - case XML_notEqual: - { - Reference< XLocaleData > xLocale( getGlobalFactory()->createInstance( - CREATE_OUSTRING("com.sun.star.i18n.LocaleData") ), UNO_QUERY ); - - if ( !xLocale.is() ) - return; - - LocaleDataItem aLocaleItem = xLocale->getLocaleItem( ::com::sun::star::lang::Locale() ); - pField->IsNumeric = lclIsNumeric(pField->StringValue, aLocaleItem); - - if ( !pField->IsNumeric && lclWildcard2Regex(pField->StringValue) ) - mbUseRegex = true; - - maFields.push_back(aItem); - } - break; - - case XML_greaterThan: - case XML_greaterThanOrEqual: - case XML_lessThan: - case XML_lessThanOrEqual: - { - pField->IsNumeric = true; - maFields.push_back(aItem); - } - break; - default: - OSL_ENSURE( false, "AutoFilterContext::importCustomFilter: unhandled case" ); + // nothing to read for BIFF_ID_AUTOFILTER + case BIFF_ID_FILTERCOLUMN: mrAutoFilter.createFilterColumn().importFilterColumn( rStrm ); break; } } -void AutoFilterContext::importFilters( const AttributeList& rAttribs ) -{ - // blank (boolean) and calendarType attributes can be present, but not used for now. - - mbShowBlank = rAttribs.getBool( XML_blank, false ); - maFilterNames.clear(); -} - -void AutoFilterContext::importFilter( const AttributeList& rAttribs ) -{ - if (mnCurColID == -1) - return; - - OUString value = rAttribs.getString( XML_val, OUString() ); - if ( value.getLength() ) - maFilterNames.push_back(value); -} - -void AutoFilterContext::importDynamicFilter( const AttributeList& /*rAttribs*/ ) -{ - // not implemented yet - Calc doesn't support this. -} - // ============================================================================ } // namespace xls diff --git a/oox/source/xls/connectionsbuffer.cxx b/oox/source/xls/connectionsbuffer.cxx index 8a1d2c718948..12df30fe493f 100755 --- a/oox/source/xls/connectionsbuffer.cxx +++ b/oox/source/xls/connectionsbuffer.cxx @@ -51,6 +51,9 @@ const sal_Int32 BIFF12_RECONNECT_AS_REQUIRED = 1; const sal_Int32 BIFF12_RECONNECT_ALWAYS = 2; const sal_Int32 BIFF12_RECONNECT_NEVER = 3; +const sal_uInt8 BIFF12_CONNECTION_SAVEPASSWORD_ON = 1; +const sal_uInt8 BIFF12_CONNECTION_SAVEPASSWORD_OFF = 2; + const sal_uInt16 BIFF12_CONNECTION_KEEPALIVE = 0x0001; const sal_uInt16 BIFF12_CONNECTION_NEW = 0x0002; const sal_uInt16 BIFF12_CONNECTION_DELETED = 0x0004; @@ -187,6 +190,7 @@ ConnectionModel::ConnectionModel() : mnId( -1 ), mnType( BIFF12_CONNECTION_UNKNOWN ), mnReconnectMethod( BIFF12_RECONNECT_AS_REQUIRED ), + mnCredentials( XML_integrated ), mnInterval( 0 ), mbKeepAlive( false ), mbNew( false ), @@ -194,7 +198,8 @@ ConnectionModel::ConnectionModel() : mbOnlyUseConnFile( false ), mbBackground( false ), mbRefreshOnLoad( false ), - mbSaveData( false ) + mbSaveData( false ), + mbSavePassword( false ) { } @@ -224,6 +229,7 @@ void Connection::importConnection( const AttributeList& rAttribs ) // type and reconnectionMethod are using the BIFF12 constants instead of XML tokens maModel.mnType = rAttribs.getInteger( XML_type, BIFF12_CONNECTION_UNKNOWN ); maModel.mnReconnectMethod = rAttribs.getInteger( XML_reconnectionMethod, BIFF12_RECONNECT_AS_REQUIRED ); + maModel.mnCredentials = rAttribs.getToken( XML_credentials, XML_integrated ); maModel.mnInterval = rAttribs.getInteger( XML_interval, 0 ); maModel.mbKeepAlive = rAttribs.getBool( XML_keepAlive, false ); maModel.mbNew = rAttribs.getBool( XML_new, false ); @@ -231,6 +237,8 @@ void Connection::importConnection( const AttributeList& rAttribs ) maModel.mbOnlyUseConnFile = rAttribs.getBool( XML_onlyUseConnectionFile, false ); maModel.mbBackground = rAttribs.getBool( XML_background, false ); maModel.mbRefreshOnLoad = rAttribs.getBool( XML_refreshOnLoad, false ); + maModel.mbSaveData = rAttribs.getBool( XML_saveData, false ); + maModel.mbSavePassword = rAttribs.getBool( XML_savePassword, false ); } void Connection::importWebPr( const AttributeList& rAttribs ) @@ -281,11 +289,13 @@ void Connection::importTable( const AttributeList& rAttribs, sal_Int32 nElement void Connection::importConnection( RecordInputStream& rStrm ) { - rStrm.skip( 4 ); sal_uInt16 nFlags, nStrFlags; + sal_uInt8 nSavePassword, nCredentials; + rStrm.skip( 2 ); + rStrm >> nSavePassword; + rStrm.skip( 1 ); maModel.mnInterval = rStrm.readuInt16(); - rStrm >> nFlags >> nStrFlags >> maModel.mnType >> maModel.mnReconnectMethod >> maModel.mnId; - rStrm.skip( 1 ); // credentials + rStrm >> nFlags >> nStrFlags >> maModel.mnType >> maModel.mnReconnectMethod >> maModel.mnId >> nCredentials; if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_SOURCEFILE ) ) rStrm >> maModel.maSourceFile; @@ -298,6 +308,9 @@ void Connection::importConnection( RecordInputStream& rStrm ) if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_SSOID ) ) rStrm >> maModel.maSsoId; + static const sal_Int32 spnCredentials[] = { XML_integrated, XML_none, XML_stored, XML_prompt }; + maModel.mnCredentials = STATIC_ARRAY_SELECT( spnCredentials, nCredentials, XML_integrated ); + maModel.mbKeepAlive = getFlag( nFlags, BIFF12_CONNECTION_KEEPALIVE ); maModel.mbNew = getFlag( nFlags, BIFF12_CONNECTION_NEW ); maModel.mbDeleted = getFlag( nFlags, BIFF12_CONNECTION_DELETED ); @@ -305,6 +318,7 @@ void Connection::importConnection( RecordInputStream& rStrm ) maModel.mbBackground = getFlag( nFlags, BIFF12_CONNECTION_BACKGROUND ); maModel.mbRefreshOnLoad = getFlag( nFlags, BIFF12_CONNECTION_REFRESHONLOAD ); maModel.mbSaveData = getFlag( nFlags, BIFF12_CONNECTION_SAVEDATA ); + maModel.mbSavePassword = nSavePassword == BIFF12_CONNECTION_SAVEPASSWORD_ON; } void Connection::importWebPr( RecordInputStream& rStrm ) @@ -370,6 +384,7 @@ void Connection::importDbQuery( BiffInputStream& rStrm ) // same type constants in all BIFF versions maModel.mnType = extractValue< sal_Int32 >( nFlags, 0, 3 ); + maModel.mbSavePassword = getFlag( nFlags, BIFF_DBQUERY_SAVEPASSWORD ); OSL_ENSURE( getFlag( nFlags, BIFF_DBQUERY_ODBC ) == (maModel.mnType == BIFF12_CONNECTION_ODBC), "Connection::importDbQuery - wrong ODBC flag" ); OSL_ENSURE( getFlag( nFlags, BIFF_DBQUERY_SQLQUERY ) != (maModel.mnType == BIFF12_CONNECTION_HTML), "Connection::importDbQuery - wrong SQL query flag" ); @@ -446,41 +461,39 @@ ConnectionsBuffer::ConnectionsBuffer( const WorkbookHelper& rHelper ) : { } -ConnectionRef ConnectionsBuffer::importConnection( const AttributeList& rAttribs ) +Connection& ConnectionsBuffer::createConnection() { ConnectionRef xConnection( new Connection( *this ) ); - xConnection->importConnection( rAttribs ); - insertConnection( xConnection ); - return xConnection; + maConnections.push_back( xConnection ); + return *xConnection; } -ConnectionRef ConnectionsBuffer::importConnection( RecordInputStream& rStrm ) +Connection& ConnectionsBuffer::createConnectionWithId() { - ConnectionRef xConnection( new Connection( *this ) ); - xConnection->importConnection( rStrm ); - insertConnection( xConnection ); - return xConnection; + ConnectionRef xConnection( new Connection( *this, mnUnusedId ) ); + maConnections.push_back( xConnection ); + insertConnectionToMap( xConnection ); + return *xConnection; } -ConnectionRef ConnectionsBuffer::createConnection() +void ConnectionsBuffer::finalizeImport() { - ConnectionRef xConnection( new Connection( *this, mnUnusedId ) ); - insertConnection( xConnection ); - return xConnection; + for( ConnectionVector::iterator aIt = maConnections.begin(), aEnd = maConnections.end(); aIt != aEnd; ++aIt ) + insertConnectionToMap( *aIt ); } ConnectionRef ConnectionsBuffer::getConnection( sal_Int32 nConnId ) const { - return maConnections.get( nConnId ); + return maConnectionsById.get( nConnId ); } -void ConnectionsBuffer::insertConnection( const ConnectionRef& rxConnection ) +void ConnectionsBuffer::insertConnectionToMap( const ConnectionRef& rxConnection ) { sal_Int32 nConnId = rxConnection->getConnectionId(); if( nConnId > 0 ) { - OSL_ENSURE( !maConnections.has( nConnId ), "ConnectionsBuffer::insertConnection - multiple connection identifier" ); - maConnections[ nConnId ] = rxConnection; + OSL_ENSURE( !maConnectionsById.has( nConnId ), "ConnectionsBuffer::insertConnectionToMap - multiple connection identifier" ); + maConnectionsById[ nConnId ] = rxConnection; mnUnusedId = ::std::max< sal_Int32 >( mnUnusedId, nConnId + 1 ); } } diff --git a/oox/source/xls/connectionsfragment.cxx b/oox/source/xls/connectionsfragment.cxx index ee4aaa3a059e..f83f9028faa8 100644 --- a/oox/source/xls/connectionsfragment.cxx +++ b/oox/source/xls/connectionsfragment.cxx @@ -29,6 +29,7 @@ #include "oox/helper/attributelist.hxx" #include "oox/xls/biffhelper.hxx" +#include "oox/xls/connectionsbuffer.hxx" namespace oox { namespace xls { @@ -41,11 +42,10 @@ using ::oox::core::RecordInfo; // ============================================================================ -ConnectionContext::ConnectionContext( WorkbookFragmentBase& rParent, const ConnectionRef& rxConnection ) : +ConnectionContext::ConnectionContext( WorkbookFragmentBase& rParent, Connection& rConnection ) : WorkbookContextBase( rParent ), - mxConnection( rxConnection ) + mrConnection( rConnection ) { - OSL_ENSURE( mxConnection.get(), "ConnectionContext::ConnectionContext - missing connection" ); } ContextHandlerRef ConnectionContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) @@ -55,7 +55,7 @@ ContextHandlerRef ConnectionContext::onCreateContext( sal_Int32 nElement, const case XLS_TOKEN( connection ): if( nElement == XLS_TOKEN( webPr ) ) { - mxConnection->importWebPr( rAttribs ); + mrConnection.importWebPr( rAttribs ); return this; } break; @@ -63,18 +63,24 @@ ContextHandlerRef ConnectionContext::onCreateContext( sal_Int32 nElement, const case XLS_TOKEN( webPr ): if( nElement == XLS_TOKEN( tables ) ) { - mxConnection->importTables( rAttribs ); + mrConnection.importTables( rAttribs ); return this; } break; case XLS_TOKEN( tables ): - mxConnection->importTable( rAttribs, nElement ); + mrConnection.importTable( rAttribs, nElement ); break; } return 0; } +void ConnectionContext::onStartElement( const AttributeList& rAttribs ) +{ + if( getCurrentElement() == XLS_TOKEN( connection ) ) + mrConnection.importConnection( rAttribs ); +} + ContextHandlerRef ConnectionContext::onCreateRecordContext( sal_Int32 nRecId, RecordInputStream& rStrm ) { switch( getCurrentElement() ) @@ -82,7 +88,7 @@ ContextHandlerRef ConnectionContext::onCreateRecordContext( sal_Int32 nRecId, Re case BIFF12_ID_CONNECTION: if( nRecId == BIFF12_ID_WEBPR ) { - mxConnection->importWebPr( rStrm ); + mrConnection.importWebPr( rStrm ); return this; } break; @@ -90,18 +96,24 @@ ContextHandlerRef ConnectionContext::onCreateRecordContext( sal_Int32 nRecId, Re case BIFF12_ID_WEBPR: if( nRecId == BIFF12_ID_WEBPRTABLES ) { - mxConnection->importWebPrTables( rStrm ); + mrConnection.importWebPrTables( rStrm ); return this; } break; case BIFF12_ID_WEBPRTABLES: - mxConnection->importWebPrTable( rStrm, nRecId ); + mrConnection.importWebPrTable( rStrm, nRecId ); break; } return 0; } +void ConnectionContext::onStartRecord( RecordInputStream& rStrm ) +{ + if( getCurrentElement() == BIFF12_ID_CONNECTION ) + mrConnection.importConnection( rStrm ); +} + // ============================================================================ ConnectionsFragment::ConnectionsFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) : @@ -109,7 +121,7 @@ ConnectionsFragment::ConnectionsFragment( const WorkbookHelper& rHelper, const O { } -ContextHandlerRef ConnectionsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) +ContextHandlerRef ConnectionsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) { switch( getCurrentElement() ) { @@ -120,17 +132,13 @@ ContextHandlerRef ConnectionsFragment::onCreateContext( sal_Int32 nElement, cons case XLS_TOKEN( connections ): if( nElement == XLS_TOKEN( connection ) ) - { - ConnectionRef xConnection = getConnections().importConnection( rAttribs ); - if( xConnection.get() ) - return new ConnectionContext( *this, xConnection ); - } + return new ConnectionContext( *this, getConnections().createConnection() ); break; } return 0; } -ContextHandlerRef ConnectionsFragment::onCreateRecordContext( sal_Int32 nRecId, RecordInputStream& rStrm ) +ContextHandlerRef ConnectionsFragment::onCreateRecordContext( sal_Int32 nRecId, RecordInputStream& /*rStrm*/ ) { switch( getCurrentElement() ) { @@ -141,11 +149,7 @@ ContextHandlerRef ConnectionsFragment::onCreateRecordContext( sal_Int32 nRecId, case BIFF12_ID_CONNECTIONS: if( nRecId == BIFF12_ID_CONNECTION ) - { - ConnectionRef xConnection = getConnections().importConnection( rStrm ); - if( xConnection.get() ) - return new ConnectionContext( *this, xConnection ); - } + return new ConnectionContext( *this, getConnections().createConnection() ); break; } return 0; @@ -164,6 +168,11 @@ const RecordInfo* ConnectionsFragment::getRecordInfos() const return spRecInfos; } +void ConnectionsFragment::finalizeImport() +{ + getConnections().finalizeImport(); +} + // ============================================================================ } // namespace xls diff --git a/oox/source/xls/defnamesbuffer.cxx b/oox/source/xls/defnamesbuffer.cxx index 6ff80e0efe64..94ca1fb321bf 100644 --- a/oox/source/xls/defnamesbuffer.cxx +++ b/oox/source/xls/defnamesbuffer.cxx @@ -81,6 +81,11 @@ const sal_uInt8 BIFF2_DEFNAME_FUNC = 0x02; /// BIFF2 function/comma const sal_uInt16 BIFF_DEFNAME_GLOBAL = 0; /// 0 = Globally defined name. +const sal_uInt16 BIFF_REFFLAG_COL1REL = 0x0001; +const sal_uInt16 BIFF_REFFLAG_ROW1REL = 0x0002; +const sal_uInt16 BIFF_REFFLAG_COL2REL = 0x0004; +const sal_uInt16 BIFF_REFFLAG_ROW2REL = 0x0008; + // ---------------------------------------------------------------------------- const sal_Char* const spcLegacyPrefix = "Excel_BuiltIn_"; @@ -88,20 +93,20 @@ const sal_Char* const spcOoxPrefix = "_xlnm."; const sal_Char* const sppcBaseNames[] = { - "Consolidate_Area", /* OOXML */ + "Consolidate_Area", "Auto_Open", "Auto_Close", - "Extract", /* OOXML */ - "Database", /* OOXML */ - "Criteria", /* OOXML */ - "Print_Area", /* OOXML */ - "Print_Titles", /* OOXML */ + "Extract", + "Database", + "Criteria", + "Print_Area", + "Print_Titles", "Recorder", "Data_Form", "Auto_Activate", "Auto_Deactivate", - "Sheet_Title", /* OOXML */ - "_FilterDatabase" /* OOXML */ + "Sheet_Title", + "_FilterDatabase" }; /** Localized names for _xlnm._FilterDatabase as used in BIFF5. */ @@ -113,7 +118,7 @@ const sal_Char* const sppcFilterDbNames[] = OUString lclGetBaseName( sal_Unicode cBuiltinId ) { - OSL_ENSURE( cBuiltinId < STATIC_ARRAY_SIZE( sppcBaseNames ), "lclGetBaseName - unknown builtin name" ); + OSL_ENSURE( cBuiltinId < STATIC_ARRAY_SIZE( sppcBaseNames ), "lclGetBaseName - unsupported built-in identifier" ); OUStringBuffer aBuffer; if( cBuiltinId < STATIC_ARRAY_SIZE( sppcBaseNames ) ) aBuffer.appendAscii( sppcBaseNames[ cBuiltinId ] ); @@ -122,67 +127,52 @@ OUString lclGetBaseName( sal_Unicode cBuiltinId ) return aBuffer.makeStringAndClear(); } -OUString lclGetBuiltinName( sal_Unicode cBuiltinId ) +OUString lclGetPrefixedName( sal_Unicode cBuiltinId ) { return OUStringBuffer().appendAscii( spcOoxPrefix ).append( lclGetBaseName( cBuiltinId ) ).makeStringAndClear(); } -sal_Unicode lclGetBuiltinIdFromOox( const OUString& rOoxName ) +/** returns the built-in name identifier from a perfixed built-in name, e.g. '_xlnm.Print_Area'. */ +sal_Unicode lclGetBuiltinIdFromPrefixedName( const OUString& rModelName ) { OUString aPrefix = OUString::createFromAscii( spcOoxPrefix ); sal_Int32 nPrefixLen = aPrefix.getLength(); - if( rOoxName.matchIgnoreAsciiCase( aPrefix ) ) + if( rModelName.matchIgnoreAsciiCase( aPrefix ) ) { for( sal_Unicode cBuiltinId = 0; cBuiltinId < STATIC_ARRAY_SIZE( sppcBaseNames ); ++cBuiltinId ) { OUString aBaseName = lclGetBaseName( cBuiltinId ); sal_Int32 nBaseNameLen = aBaseName.getLength(); - if( (rOoxName.getLength() == nPrefixLen + nBaseNameLen) && rOoxName.matchIgnoreAsciiCase( aBaseName, nPrefixLen ) ) + if( (rModelName.getLength() == nPrefixLen + nBaseNameLen) && rModelName.matchIgnoreAsciiCase( aBaseName, nPrefixLen ) ) return cBuiltinId; } } return BIFF_DEFNAME_UNKNOWN; } -sal_Unicode lclGetBuiltinIdFromBiff( const OUString& rName ) +/** returns the built-in name identifier from a built-in base name, e.g. 'Print_Area'. */ +sal_Unicode lclGetBuiltinIdFromBaseName( const OUString& rModelName ) { for( sal_Unicode cBuiltinId = 0; cBuiltinId < STATIC_ARRAY_SIZE( sppcBaseNames ); ++cBuiltinId ) - if( rName.equalsIgnoreAsciiCaseAscii( sppcBaseNames[ cBuiltinId ] ) ) + if( rModelName.equalsIgnoreAsciiCaseAscii( sppcBaseNames[ cBuiltinId ] ) ) return cBuiltinId; return BIFF_DEFNAME_UNKNOWN; } -bool lclIsFilterDatabaseName( const OUString& rName ) +bool lclIsFilterDatabaseName( const OUString& rModelName ) { for( const sal_Char* const* ppcName = sppcFilterDbNames; ppcName < STATIC_ARRAY_END( sppcFilterDbNames ); ++ppcName ) - if( rName.equalsIgnoreAsciiCaseAscii( *ppcName ) ) + if( rModelName.equalsIgnoreAsciiCaseAscii( *ppcName ) ) return true; return false; } -} // namespace - -// ============================================================================ - -DefinedNameModel::DefinedNameModel() : - mnSheet( -1 ), - mnFuncGroupId( -1 ), - mbMacro( false ), - mbFunction( false ), - mbVBName( false ), - mbHidden( false ) +OUString lclGetUpcaseModelName( const OUString& rModelName ) { + // TODO: i18n? + return rModelName.toAsciiUpperCase(); } -// ============================================================================ - -namespace { - -const sal_uInt16 BIFF_REFFLAG_COL1REL = 0x0001; -const sal_uInt16 BIFF_REFFLAG_ROW1REL = 0x0002; -const sal_uInt16 BIFF_REFFLAG_COL2REL = 0x0004; -const sal_uInt16 BIFF_REFFLAG_ROW2REL = 0x0008; - void lclConvertRefFlags( sal_Int32& ornFlags, sal_Int32& ornAbsPos, sal_Int32& ornRelPos, sal_Int32 nBasePos, sal_Int32 nApiRelFlag, bool bRel ) { if( getFlag( ornFlags, nApiRelFlag ) && !bRel ) @@ -232,7 +222,19 @@ Any lclConvertReference( const Any& rRefAny, const CellAddress& rBaseAddress, sa } // namespace -// ---------------------------------------------------------------------------- +// ============================================================================ + +DefinedNameModel::DefinedNameModel() : + mnSheet( -1 ), + mnFuncGroupId( -1 ), + mbMacro( false ), + mbFunction( false ), + mbVBName( false ), + mbHidden( false ) +{ +} + +// ============================================================================ DefinedNameBase::DefinedNameBase( const WorkbookHelper& rHelper ) : WorkbookHelper( rHelper ) @@ -242,7 +244,7 @@ DefinedNameBase::DefinedNameBase( const WorkbookHelper& rHelper ) : const OUString& DefinedNameBase::getUpcaseModelName() const { if( maUpModelName.getLength() == 0 ) - maUpModelName = maModel.maName.toAsciiUpperCase(); + maUpModelName = lclGetUpcaseModelName( maModel.maName ); return maUpModelName; } @@ -325,8 +327,11 @@ void DefinedName::importDefinedName( const AttributeList& rAttribs ) maModel.mbFunction = rAttribs.getBool( XML_function, false ); maModel.mbVBName = rAttribs.getBool( XML_vbProcedure, false ); maModel.mbHidden = rAttribs.getBool( XML_hidden, false ); - mcBuiltinId = lclGetBuiltinIdFromOox( maModel.maName ); mnCalcSheet = (maModel.mnSheet >= 0) ? getWorksheets().getCalcSheetIndex( maModel.mnSheet ) : -1; + + /* Detect built-in state from name itself, there is no built-in flag. + Built-in names are prexixed with '_xlnm.' instead. */ + mcBuiltinId = lclGetBuiltinIdFromPrefixedName( maModel.maName ); } void DefinedName::setFormula( const OUString& rFormula ) @@ -349,12 +354,9 @@ void DefinedName::importDefinedName( RecordInputStream& rStrm ) maModel.mbVBName = getFlag( nFlags, BIFF12_DEFNAME_VBNAME ); maModel.mbHidden = getFlag( nFlags, BIFF12_DEFNAME_HIDDEN ); - // get builtin name index from name + // get built-in name index from name if( getFlag( nFlags, BIFF12_DEFNAME_BUILTIN ) ) - mcBuiltinId = lclGetBuiltinIdFromBiff( maModel.maName ); - // unhide built-in names (_xlnm._FilterDatabase is always hidden) - if( isBuiltinName() ) - maModel.mbHidden = false; + mcBuiltinId = lclGetBuiltinIdFromBaseName( maModel.maName ); // store token array data sal_Int64 nRecPos = rStrm.tell(); @@ -416,24 +418,22 @@ void DefinedName::importDefinedName( BiffInputStream& rStrm, sal_Int16 nCalcShee maModel.mbVBName = getFlag( nFlags, BIFF_DEFNAME_VBNAME ); maModel.mbHidden = getFlag( nFlags, BIFF_DEFNAME_HIDDEN ); - // get builtin name index from name + // get built-in name index from name if( getFlag( nFlags, BIFF_DEFNAME_BUILTIN ) ) { - OSL_ENSURE( maModel.maName.getLength() == 1, "DefinedName::importDefinedName - wrong builtin name" ); - if( maModel.maName.getLength() > 0 ) + // name may be the built-in identifier or the built-in base name + if( maModel.maName.getLength() == 1 ) mcBuiltinId = maModel.maName[ 0 ]; + else + mcBuiltinId = lclGetBuiltinIdFromBaseName( maModel.maName ); } - /* In BIFF5, _xlnm._FilterDatabase appears as hidden user name without + /* In BIFF5, '_FilterDatabase' appears as hidden user name without built-in flag, and even worse, localized. */ else if( (eBiff == BIFF5) && lclIsFilterDatabaseName( maModel.maName ) ) { mcBuiltinId = BIFF_DEFNAME_FILTERDATABASE; } - // unhide built-in names (_xlnm._FilterDatabase is always hidden) - if( isBuiltinName() ) - maModel.mbHidden = false; - // get sheet index for sheet-local names in BIFF5-BIFF8 switch( getBiff() ) { @@ -483,18 +483,13 @@ void DefinedName::importDefinedName( BiffInputStream& rStrm, sal_Int16 nCalcShee void DefinedName::createNameObject() { - // do not create names for (macro) functions + // do not create names for (macro) functions or VBA procedures // #163146# do not ignore hidden names (may be regular names created by VBA scripts) - if( /*maModel.mbHidden ||*/ maModel.mbFunction ) + if( /*maModel.mbHidden ||*/ maModel.mbFunction || maModel.mbVBName ) return; - // convert original name to final Calc name - if( maModel.mbVBName ) - maCalcName = maModel.maName; - else if( isBuiltinName() ) - maCalcName = lclGetBuiltinName( mcBuiltinId ); - else - maCalcName = maModel.maName; //! TODO convert to valid name + // convert original name to final Calc name (TODO: filter invalid characters from model name) + maCalcName = isBuiltinName() ? lclGetPrefixedName( mcBuiltinId ) : maModel.maName; // #163146# do not rename sheet-local names by default, this breaks VBA scripts #if 0 @@ -544,7 +539,7 @@ void DefinedName::convertFormula() case FILTER_UNKNOWN: break; } - // set builtin names (print ranges, repeated titles, filter ranges) + // set built-in names (print ranges, repeated titles, filter ranges) if( !isGlobalName() ) switch( mcBuiltinId ) { case BIFF_DEFNAME_PRINTAREA: @@ -652,14 +647,20 @@ void DefinedNamesBuffer::importDefinedName( BiffInputStream& rStrm ) void DefinedNamesBuffer::finalizeImport() { - // first insert all names without formula definition into the document + // first insert all names without formula definition into the document, and insert them into the maps for( DefNameVector::iterator aIt = maDefNames.begin(), aEnd = maDefNames.end(); aIt != aEnd; ++aIt ) { DefinedNameRef xDefName = *aIt; xDefName->createNameObject(); + // map by sheet index and original model name + maModelNameMap[ SheetNameKey( xDefName->getLocalCalcSheet(), xDefName->getUpcaseModelName() ) ] = xDefName; + // map by sheet index and built-in identifier + if( !xDefName->isGlobalName() && xDefName->isBuiltinName() ) + maBuiltinMap[ BuiltinKey( xDefName->getLocalCalcSheet(), xDefName->getBuiltinId() ) ] = xDefName; + // map by API formula token identifier sal_Int32 nTokenIndex = xDefName->getTokenIndex(); if( nTokenIndex >= 0 ) - maDefNameMap[ nTokenIndex ] = xDefName; + maTokenIdMap[ nTokenIndex ] = xDefName; } /* Now convert all name formulas, so that the formula parser can find all @@ -674,25 +675,22 @@ DefinedNameRef DefinedNamesBuffer::getByIndex( sal_Int32 nIndex ) const DefinedNameRef DefinedNamesBuffer::getByTokenIndex( sal_Int32 nIndex ) const { - return maDefNameMap.get( nIndex ); + return maTokenIdMap.get( nIndex ); } DefinedNameRef DefinedNamesBuffer::getByModelName( const OUString& rModelName, sal_Int16 nCalcSheet ) const { - DefinedNameRef xGlobalName; // a found global name - DefinedNameRef xLocalName; // a found local name - for( DefNameVector::const_iterator aIt = maDefNames.begin(), aEnd = maDefNames.end(); (aIt != aEnd) && !xLocalName; ++aIt ) - { - DefinedNameRef xCurrName = *aIt; - if( xCurrName->getModelName() == rModelName ) - { - if( xCurrName->getLocalCalcSheet() == nCalcSheet ) - xLocalName = xCurrName; - else if( xCurrName->isGlobalName() ) - xGlobalName = xCurrName; - } - } - return xLocalName.get() ? xLocalName : xGlobalName; + OUString aUpcaseName = lclGetUpcaseModelName( rModelName ); + DefinedNameRef xDefName = maModelNameMap.get( SheetNameKey( nCalcSheet, aUpcaseName ) ); + // lookup global name, if no local name exists + if( !xDefName && (nCalcSheet >= 0) ) + xDefName = maModelNameMap.get( SheetNameKey( -1, aUpcaseName ) ); + return xDefName; +} + +DefinedNameRef DefinedNamesBuffer::getByBuiltinId( sal_Unicode cBuiltinId, sal_Int16 nCalcSheet ) const +{ + return maBuiltinMap.get( BuiltinKey( nCalcSheet, cBuiltinId ) ); } DefinedNameRef DefinedNamesBuffer::createDefinedName() diff --git a/oox/source/xls/makefile.mk b/oox/source/xls/makefile.mk index b6d69dc5cf8c..6ca6e6a271b9 100644 --- a/oox/source/xls/makefile.mk +++ b/oox/source/xls/makefile.mk @@ -42,6 +42,7 @@ ENABLE_EXCEPTIONS=TRUE SLOFILES = \ $(SLO)$/addressconverter.obj \ + $(SLO)$/autofilterbuffer.obj \ $(SLO)$/autofiltercontext.obj \ $(SLO)$/biffcodec.obj \ $(SLO)$/biffdetector.obj \ @@ -94,7 +95,7 @@ SLOFILES = \ $(SLO)$/workbooksettings.obj \ $(SLO)$/worksheetbuffer.obj \ $(SLO)$/worksheetfragment.obj \ - $(SLO)$/worksheethelper.obj \ + $(SLO)$/worksheethelper.obj \ $(SLO)$/worksheetsettings.obj # --- Targets ------------------------------------------------------- diff --git a/oox/source/xls/pivotcachefragment.cxx b/oox/source/xls/pivotcachefragment.cxx index 3d69928f7ee8..8c4f56c7c820 100644 --- a/oox/source/xls/pivotcachefragment.cxx +++ b/oox/source/xls/pivotcachefragment.cxx @@ -387,9 +387,8 @@ bool BiffPivotCacheFragment::importFragment() // ============================================================================ -BiffPivotCacheRecordsContext::BiffPivotCacheRecordsContext( - const BiffWorkbookFragmentBase& rFragment, const PivotCache& rPivotCache ) : - BiffWorksheetContextBase( rFragment, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET, rPivotCache.getSourceRange().Sheet ), +BiffPivotCacheRecordsContext::BiffPivotCacheRecordsContext( const WorkbookHelper& rHelper, const PivotCache& rPivotCache ) : + BiffWorksheetContextBase( rHelper, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET, rPivotCache.getSourceRange().Sheet ), mrPivotCache( rPivotCache ), mnColIdx( 0 ), mnRow( 0 ), diff --git a/oox/source/xls/pivottablefragment.cxx b/oox/source/xls/pivottablefragment.cxx index 1486a600e131..1ed95b141b61 100644 --- a/oox/source/xls/pivottablefragment.cxx +++ b/oox/source/xls/pivottablefragment.cxx @@ -294,8 +294,8 @@ const RecordInfo* PivotTableFragment::getRecordInfos() const // ============================================================================ // ============================================================================ -BiffPivotTableContext::BiffPivotTableContext( const BiffWorksheetFragmentBase& rFragment ) : - BiffWorksheetContextBase( rFragment ), +BiffPivotTableContext::BiffPivotTableContext( const WorksheetHelper& rHelper ) : + BiffWorksheetContextBase( rHelper ), mrPivotTable( getPivotTables().createPivotTable() ) { } diff --git a/oox/source/xls/querytablebuffer.cxx b/oox/source/xls/querytablebuffer.cxx index 8f65fe1438db..2a4df73287ca 100644 --- a/oox/source/xls/querytablebuffer.cxx +++ b/oox/source/xls/querytablebuffer.cxx @@ -283,18 +283,14 @@ void QueryTable::importQueryTable( BiffInputStream& rStrm ) // create a new connection object that will store settings from following records OSL_ENSURE( maModel.mnConnId == -1, "QueryTable::importQueryTable - multiple call" ); - ConnectionRef xConnection = getConnections().createConnection(); - OSL_ENSURE( xConnection.get(), "QueryTable::importQueryTable - cannot create connection object" ); - if( xConnection.get() ) - { - maModel.mnConnId = xConnection->getConnectionId(); - - // a DBQUERY record with some PCITEM_STRING records must follow - bool bHasDbQuery = (rStrm.getNextRecId() == BIFF_ID_DBQUERY) && rStrm.startNextRecord(); - OSL_ENSURE( bHasDbQuery, "QueryTable::importQueryTable - missing DBQUERY record" ); - if( bHasDbQuery ) - xConnection->importDbQuery( rStrm ); - } + Connection& rConnection = getConnections().createConnectionWithId(); + maModel.mnConnId = rConnection.getConnectionId(); + + // a DBQUERY record with some PCITEM_STRING records must follow + bool bHasDbQuery = (rStrm.getNextRecId() == BIFF_ID_DBQUERY) && rStrm.startNextRecord(); + OSL_ENSURE( bHasDbQuery, "QueryTable::importQueryTable - missing DBQUERY record" ); + if( bHasDbQuery ) + rConnection.importDbQuery( rStrm ); } void QueryTable::importQueryTableRefresh( BiffInputStream& rStrm ) diff --git a/oox/source/xls/querytablefragment.cxx b/oox/source/xls/querytablefragment.cxx index 493fb22bc632..2f4b2d06ff11 100644 --- a/oox/source/xls/querytablefragment.cxx +++ b/oox/source/xls/querytablefragment.cxx @@ -84,8 +84,8 @@ const RecordInfo* QueryTableFragment::getRecordInfos() const // ============================================================================ -BiffQueryTableContext::BiffQueryTableContext( const BiffWorksheetFragmentBase& rFragment ) : - BiffWorksheetContextBase( rFragment ), +BiffQueryTableContext::BiffQueryTableContext( const WorksheetHelper& rHelper ) : + BiffWorksheetContextBase( rHelper ), mrQueryTable( getQueryTables().createQueryTable() ) { } diff --git a/oox/source/xls/sheetdatacontext.cxx b/oox/source/xls/sheetdatacontext.cxx index e132ec74b689..258ec995e9e0 100644 --- a/oox/source/xls/sheetdatacontext.cxx +++ b/oox/source/xls/sheetdatacontext.cxx @@ -77,9 +77,6 @@ const sal_uInt16 BIFF12_ROW_CUSTOMHEIGHT = 0x2000; const sal_uInt16 BIFF12_ROW_CUSTOMFORMAT = 0x4000; const sal_uInt8 BIFF12_ROW_SHOWPHONETIC = 0x01; -const sal_uInt8 BIFF_BOOLERR_BOOL = 0; -const sal_uInt8 BIFF_BOOLERR_ERROR = 1; - const sal_uInt16 BIFF_DATATABLE_ROW = 0x0004; const sal_uInt16 BIFF_DATATABLE_2D = 0x0008; const sal_uInt16 BIFF_DATATABLE_REF1DEL = 0x0010; @@ -591,8 +588,8 @@ void SheetDataContext::importDataTable( RecordInputStream& rStrm ) // ============================================================================ -BiffSheetDataContext::BiffSheetDataContext( const BiffWorksheetFragmentBase& rParent ) : - BiffWorksheetContextBase( rParent ), +BiffSheetDataContext::BiffSheetDataContext( const WorksheetHelper& rHelper ) : + BiffWorksheetContextBase( rHelper ), mnBiff2XfId( 0 ) { mnArrayIgnoreSize = (getBiff() == BIFF2) ? 1 : ((getBiff() <= BIFF4) ? 2 : 6); diff --git a/oox/source/xls/tablebuffer.cxx b/oox/source/xls/tablebuffer.cxx index 1d4444986adc..53408f366ae5 100644 --- a/oox/source/xls/tablebuffer.cxx +++ b/oox/source/xls/tablebuffer.cxx @@ -28,9 +28,7 @@ #include "oox/xls/tablebuffer.hxx" #include <com/sun/star/sheet/XDatabaseRange.hpp> -#include <com/sun/star/sheet/XDatabaseRanges.hpp> #include "oox/helper/attributelist.hxx" -#include "oox/helper/containerhelper.hxx" #include "oox/helper/propertyset.hxx" #include "oox/helper/recordinputstream.hxx" #include "oox/xls/addressconverter.hxx" @@ -61,6 +59,7 @@ TableModel::TableModel() : Table::Table( const WorkbookHelper& rHelper ) : WorkbookHelper( rHelper ), + maAutoFilters( rHelper ), mnTokenIndex( -1 ) { } @@ -91,24 +90,20 @@ void Table::importTable( RecordInputStream& rStrm, sal_Int16 nSheet ) void Table::finalizeImport() { - // validate cell range - maDestRange = maModel.maRange; - bool bValidRange = getAddressConverter().validateCellRange( maDestRange, true, true ); - // create database range - if( bValidRange && (maModel.mnId > 0) && (maModel.maDisplayName.getLength() > 0) ) try + if( (maModel.mnId > 0) && (maModel.maDisplayName.getLength() > 0) ) try { - // find an unused name - PropertySet aDocProps( getDocument() ); - Reference< XDatabaseRanges > xDatabaseRanges( aDocProps.getAnyProperty( PROP_DatabaseRanges ), UNO_QUERY_THROW ); - Reference< XNameAccess > xNameAccess( xDatabaseRanges, UNO_QUERY_THROW ); - OUString aName = ContainerHelper::getUnusedName( xNameAccess, maModel.maDisplayName, '_' ); - xDatabaseRanges->addNewByName( aName, maModel.maRange ); - Reference< XDatabaseRange > xDatabaseRange( xDatabaseRanges->getByName( aName ), UNO_QUERY_THROW ); + maDBRangeName = maModel.maDisplayName; + Reference< XDatabaseRange > xDatabaseRange( createDatabaseRangeObject( maDBRangeName, maModel.maRange ), UNO_SET_THROW ); + maDestRange = xDatabaseRange->getDataArea(); + // get formula token index of the database range PropertySet aPropSet( xDatabaseRange ); if( !aPropSet.getProperty( mnTokenIndex, PROP_TokenIndex ) ) mnTokenIndex = -1; + + // filter settings + maAutoFilters.finalizeImport( xDatabaseRange ); } catch( Exception& ) { @@ -123,24 +118,19 @@ TableBuffer::TableBuffer( const WorkbookHelper& rHelper ) : { } -TableRef TableBuffer::importTable( const AttributeList& rAttribs, sal_Int16 nSheet ) -{ - TableRef xTable( new Table( *this ) ); - xTable->importTable( rAttribs, nSheet ); - insertTable( xTable ); - return xTable; -} - -TableRef TableBuffer::importTable( RecordInputStream& rStrm, sal_Int16 nSheet ) +Table& TableBuffer::createTable() { - TableRef xTable( new Table( *this ) ); - xTable->importTable( rStrm, nSheet ); - insertTable( xTable ); - return xTable; + TableVector::value_type xTable( new Table( *this ) ); + maTables.push_back( xTable ); + return *xTable; } void TableBuffer::finalizeImport() { + // map all tables by identifier and display name + for( TableVector::iterator aIt = maTables.begin(), aEnd = maTables.end(); aIt != aEnd; ++aIt ) + insertTableToMaps( *aIt ); + // finalize all valid tables maIdTables.forEachMem( &Table::finalizeImport ); } @@ -156,15 +146,15 @@ TableRef TableBuffer::getTable( const OUString& rDispName ) const // private -------------------------------------------------------------------- -void TableBuffer::insertTable( const TableRef& rxTable ) +void TableBuffer::insertTableToMaps( const TableRef& rxTable ) { sal_Int32 nTableId = rxTable->getTableId(); const OUString& rDispName = rxTable->getDisplayName(); if( (nTableId > 0) && (rDispName.getLength() > 0) ) { - OSL_ENSURE( !maIdTables.has( nTableId ), "TableBuffer::insertTable - multiple table identifier" ); + OSL_ENSURE( !maIdTables.has( nTableId ), "TableBuffer::insertTableToMaps - multiple table identifier" ); maIdTables[ nTableId ] = rxTable; - OSL_ENSURE( !maNameTables.has( rDispName ), "TableBuffer::insertTable - multiple table name" ); + OSL_ENSURE( !maNameTables.has( rDispName ), "TableBuffer::insertTableToMaps - multiple table name" ); maNameTables[ rDispName ] = rxTable; } } diff --git a/oox/source/xls/tablefragment.cxx b/oox/source/xls/tablefragment.cxx index 54415081a288..853a3d73ecd0 100644 --- a/oox/source/xls/tablefragment.cxx +++ b/oox/source/xls/tablefragment.cxx @@ -27,6 +27,10 @@ #include "oox/xls/tablefragment.hxx" +#include "oox/xls/autofilterbuffer.hxx" +#include "oox/xls/autofiltercontext.hxx" +#include "oox/xls/tablebuffer.hxx" + namespace oox { namespace xls { @@ -39,7 +43,8 @@ using ::rtl::OUString; // ============================================================================ TableFragment::TableFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) : - WorksheetFragmentBase( rHelper, rFragmentPath ) + WorksheetFragmentBase( rHelper, rFragmentPath ), + mrTable( getTables().createTable() ) { } @@ -49,7 +54,14 @@ ContextHandlerRef TableFragment::onCreateContext( sal_Int32 nElement, const Attr { case XML_ROOT_CONTEXT: if( nElement == XLS_TOKEN( table ) ) - mxTable = getTables().importTable( rAttribs, getSheetIndex() ); + { + mrTable.importTable( rAttribs, getSheetIndex() ); + return this; + } + break; + case XLS_TOKEN( table ): + if( nElement == XLS_TOKEN( autoFilter ) ) + return new AutoFilterContext( *this, mrTable.createAutoFilter() ); break; } return 0; @@ -61,7 +73,14 @@ ContextHandlerRef TableFragment::onCreateRecordContext( sal_Int32 nRecId, Record { case XML_ROOT_CONTEXT: if( nRecId == BIFF12_ID_TABLE ) - mxTable = getTables().importTable( rStrm, getSheetIndex() ); + { + mrTable.importTable( rStrm, getSheetIndex() ); + return this; + } + break; + case BIFF12_ID_TABLE: + if( nRecId == BIFF12_ID_AUTOFILTER ) + return new AutoFilterContext( *this, mrTable.createAutoFilter() ); break; } return 0; @@ -71,8 +90,12 @@ const RecordInfo* TableFragment::getRecordInfos() const { static const RecordInfo spRecInfos[] = { - { BIFF12_ID_TABLE, BIFF12_ID_TABLE + 1 }, - { -1, -1 } + { BIFF12_ID_AUTOFILTER, BIFF12_ID_AUTOFILTER + 1 }, + { BIFF12_ID_CUSTOMFILTERS, BIFF12_ID_CUSTOMFILTERS + 1 }, + { BIFF12_ID_DISCRETEFILTERS, BIFF12_ID_DISCRETEFILTERS + 1 }, + { BIFF12_ID_FILTERCOLUMN, BIFF12_ID_FILTERCOLUMN + 1 }, + { BIFF12_ID_TABLE, BIFF12_ID_TABLE + 1 }, + { -1, -1 } }; return spRecInfos; } diff --git a/oox/source/xls/workbookhelper.cxx b/oox/source/xls/workbookhelper.cxx index aa29e45c1d19..e523d6e42111 100644 --- a/oox/source/xls/workbookhelper.cxx +++ b/oox/source/xls/workbookhelper.cxx @@ -30,6 +30,8 @@ #include <com/sun/star/container/XIndexAccess.hpp> #include <com/sun/star/container/XNameContainer.hpp> #include <com/sun/star/document/XActionLockable.hpp> +#include <com/sun/star/sheet/XDatabaseRange.hpp> +#include <com/sun/star/sheet/XDatabaseRanges.hpp> #include <com/sun/star/sheet/XNamedRange.hpp> #include <com/sun/star/sheet/XNamedRanges.hpp> #include <com/sun/star/sheet/XSpreadsheet.hpp> @@ -136,6 +138,8 @@ public: Reference< XStyle > getStyleObject( const OUString& rStyleName, bool bPageStyle ) const; /** Creates and returns a defined name on-the-fly in the Calc document. */ Reference< XNamedRange > createNamedRangeObject( OUString& orName, sal_Int32 nNameFlags ) const; + /** Creates and returns a database range on-the-fly in the Calc document. */ + Reference< XDatabaseRange > createDatabaseRangeObject( OUString& orName, const CellRangeAddress& rRangeAddr ) const; /** Creates and returns a com.sun.star.style.Style object for cells or pages. */ Reference< XStyle > createStyleObject( OUString& orStyleName, bool bPageStyle ) const; @@ -350,17 +354,16 @@ Reference< XStyle > WorkbookData::getStyleObject( const OUString& rStyleName, bo Reference< XNamedRange > WorkbookData::createNamedRangeObject( OUString& orName, sal_Int32 nNameFlags ) const { - // find an unused name - PropertySet aDocProps( mxDoc ); - Reference< XNamedRanges > xNamedRanges( aDocProps.getAnyProperty( PROP_NamedRanges ), UNO_QUERY ); - Reference< XNameAccess > xNameAccess( xNamedRanges, UNO_QUERY ); - if( xNameAccess.is() ) - orName = ContainerHelper::getUnusedName( xNameAccess, orName, '_' ); - // create the name and insert it into the Calc document Reference< XNamedRange > xNamedRange; - if( xNamedRanges.is() && (orName.getLength() > 0) ) try + if( orName.getLength() > 0 ) try { + // find an unused name + PropertySet aDocProps( mxDoc ); + Reference< XNamedRanges > xNamedRanges( aDocProps.getAnyProperty( PROP_NamedRanges ), UNO_QUERY_THROW ); + Reference< XNameAccess > xNameAccess( xNamedRanges, UNO_QUERY_THROW ); + orName = ContainerHelper::getUnusedName( xNameAccess, orName, '_' ); + // create the named range xNamedRanges->addNewByName( orName, OUString(), CellAddress( 0, 0, 0 ), nNameFlags ); xNamedRange.set( xNamedRanges->getByName( orName ), UNO_QUERY ); } @@ -371,6 +374,32 @@ Reference< XNamedRange > WorkbookData::createNamedRangeObject( OUString& orName, return xNamedRange; } +Reference< XDatabaseRange > WorkbookData::createDatabaseRangeObject( OUString& orName, const CellRangeAddress& rRangeAddr ) const +{ + // validate cell range + CellRangeAddress aDestRange = rRangeAddr; + bool bValidRange = getAddressConverter().validateCellRange( aDestRange, true, true ); + + // create database range and insert it into the Calc document + Reference< XDatabaseRange > xDatabaseRange; + if( bValidRange && (orName.getLength() > 0) ) try + { + // find an unused name + PropertySet aDocProps( mxDoc ); + Reference< XDatabaseRanges > xDatabaseRanges( aDocProps.getAnyProperty( PROP_DatabaseRanges ), UNO_QUERY_THROW ); + Reference< XNameAccess > xNameAccess( xDatabaseRanges, UNO_QUERY_THROW ); + orName = ContainerHelper::getUnusedName( xNameAccess, orName, '_' ); + // create the database range + xDatabaseRanges->addNewByName( orName, aDestRange ); + xDatabaseRange.set( xDatabaseRanges->getByName( orName ), UNO_QUERY ); + } + catch( Exception& ) + { + } + OSL_ENSURE( xDatabaseRange.is(), "WorkbookData::createDatabaseRangeObject - cannot create database range" ); + return xDatabaseRange; +} + Reference< XStyle > WorkbookData::createStyleObject( OUString& orStyleName, bool bPageStyle ) const { Reference< XStyle > xStyle; @@ -714,6 +743,11 @@ Reference< XNamedRange > WorkbookHelper::createNamedRangeObject( OUString& orNam return mrBookData.createNamedRangeObject( orName, nNameFlags ); } +Reference< XDatabaseRange > WorkbookHelper::createDatabaseRangeObject( OUString& orName, const CellRangeAddress& rRangeAddr ) const +{ + return mrBookData.createDatabaseRangeObject( orName, rRangeAddr ); +} + Reference< XStyle > WorkbookHelper::createStyleObject( OUString& orStyleName, bool bPageStyle ) const { return mrBookData.createStyleObject( orStyleName, bPageStyle ); diff --git a/oox/source/xls/worksheetfragment.cxx b/oox/source/xls/worksheetfragment.cxx index 19dc8e34c70f..dc51981fe230 100644 --- a/oox/source/xls/worksheetfragment.cxx +++ b/oox/source/xls/worksheetfragment.cxx @@ -32,6 +32,7 @@ #include "oox/helper/attributelist.hxx" #include "oox/helper/recordinputstream.hxx" #include "oox/xls/addressconverter.hxx" +#include "oox/xls/autofilterbuffer.hxx" #include "oox/xls/autofiltercontext.hxx" #include "oox/xls/biffinputstream.hxx" #include "oox/xls/commentsfragment.hxx" @@ -267,9 +268,10 @@ ContextHandlerRef WorksheetFragment::onCreateContext( sal_Int32 nElement, const switch( nElement ) { case XLS_TOKEN( sheetData ): return new SheetDataContext( *this ); - case XLS_TOKEN( autoFilter ): return new AutoFilterContext( *this ); case XLS_TOKEN( conditionalFormatting ): return new CondFormatContext( *this ); case XLS_TOKEN( dataValidations ): return new DataValidationsContext( *this ); + case XLS_TOKEN( autoFilter ): return new AutoFilterContext( *this, getAutoFilters().createAutoFilter() ); + case XLS_TOKEN( scenarios ): return new ScenariosContext( *this ); case XLS_TOKEN( sheetViews ): case XLS_TOKEN( cols ): @@ -292,8 +294,6 @@ ContextHandlerRef WorksheetFragment::onCreateContext( sal_Int32 nElement, const case XLS_TOKEN( picture ): getPageSettings().importPicture( getRelations(), rAttribs ); break; case XLS_TOKEN( drawing ): importDrawing( rAttribs ); break; case XLS_TOKEN( legacyDrawing ): importLegacyDrawing( rAttribs ); break; - case XLS_TOKEN( scenarios ): - return new ScenariosContext( *this ); } break; @@ -387,6 +387,7 @@ ContextHandlerRef WorksheetFragment::onCreateRecordContext( sal_Int32 nRecId, Re case BIFF12_ID_SHEETDATA: return new SheetDataContext( *this ); case BIFF12_ID_CONDFORMATTING: return new CondFormatContext( *this ); case BIFF12_ID_DATAVALIDATIONS: return new DataValidationsContext( *this ); + case BIFF12_ID_AUTOFILTER: return new AutoFilterContext( *this, getAutoFilters().createAutoFilter() ); case BIFF12_ID_SCENARIOS: return new ScenariosContext( *this ); case BIFF12_ID_SHEETVIEWS: @@ -453,16 +454,20 @@ const RecordInfo* WorksheetFragment::getRecordInfos() const { static const RecordInfo spRecInfos[] = { + { BIFF12_ID_AUTOFILTER, BIFF12_ID_AUTOFILTER + 1 }, { BIFF12_ID_CFRULE, BIFF12_ID_CFRULE + 1 }, { BIFF12_ID_COLBREAKS, BIFF12_ID_COLBREAKS + 1 }, { BIFF12_ID_COLORSCALE, BIFF12_ID_COLORSCALE + 1 }, { BIFF12_ID_COLS, BIFF12_ID_COLS + 1 }, { BIFF12_ID_CONDFORMATTING, BIFF12_ID_CONDFORMATTING + 1 }, { BIFF12_ID_CONTROLS, BIFF12_ID_CONTROLS + 2 }, + { BIFF12_ID_CUSTOMFILTERS, BIFF12_ID_CUSTOMFILTERS + 1 }, { BIFF12_ID_CUSTOMSHEETVIEW, BIFF12_ID_CUSTOMSHEETVIEW + 1 }, { BIFF12_ID_CUSTOMSHEETVIEWS, BIFF12_ID_CUSTOMSHEETVIEWS + 3 }, { BIFF12_ID_DATABAR, BIFF12_ID_DATABAR + 1 }, { BIFF12_ID_DATAVALIDATIONS, BIFF12_ID_DATAVALIDATIONS + 1 }, + { BIFF12_ID_DISCRETEFILTERS, BIFF12_ID_DISCRETEFILTERS + 1 }, + { BIFF12_ID_FILTERCOLUMN, BIFF12_ID_FILTERCOLUMN + 1 }, { BIFF12_ID_HEADERFOOTER, BIFF12_ID_HEADERFOOTER + 1 }, { BIFF12_ID_ICONSET, BIFF12_ID_ICONSET + 1 }, { BIFF12_ID_MERGECELLS, BIFF12_ID_MERGECELLS + 1 }, @@ -862,6 +867,7 @@ bool BiffWorksheetFragment::importFragment() case BIFF5: switch( nRecId ) { + case BIFF_ID_AUTOFILTER: importAutoFilter( rStrm ); break; case BIFF_ID_COLINFO: importColInfo( rStrm ); break; case BIFF3_ID_DEFROWHEIGHT: importDefRowHeight( rStrm ); break; case BIFF_ID_HCENTER: rPageSett.importHorCenter( rStrm ); break; @@ -882,6 +888,7 @@ bool BiffWorksheetFragment::importFragment() case BIFF8: switch( nRecId ) { + case BIFF_ID_AUTOFILTER: importAutoFilter( rStrm ); break; case BIFF_ID_CFHEADER: rCondFormats.importCfHeader( rStrm ); break; case BIFF_ID_CODENAME: rWorksheetSett.importCodeName( rStrm ); break; case BIFF_ID_COLINFO: importColInfo( rStrm ); break; @@ -941,6 +948,12 @@ bool BiffWorksheetFragment::importFragment() // private -------------------------------------------------------------------- +void BiffWorksheetFragment::importAutoFilter( BiffInputStream& rStrm ) +{ + mxContext.reset( new BiffAutoFilterContext( *this, getAutoFilters().createAutoFilter() ) ); + mxContext->importRecord( rStrm ); +} + void BiffWorksheetFragment::importColInfo( BiffInputStream& rStrm ) { sal_uInt16 nFirstCol, nLastCol, nWidth, nXfId, nFlags; diff --git a/oox/source/xls/worksheethelper.cxx b/oox/source/xls/worksheethelper.cxx index 48a83ab07f11..de3b540df0a8 100644 --- a/oox/source/xls/worksheethelper.cxx +++ b/oox/source/xls/worksheethelper.cxx @@ -59,6 +59,7 @@ #include "oox/helper/containerhelper.hxx" #include "oox/helper/propertyset.hxx" #include "oox/xls/addressconverter.hxx" +#include "oox/xls/autofilterbuffer.hxx" #include "oox/xls/commentsbuffer.hxx" #include "oox/xls/condformatbuffer.hxx" #include "oox/xls/drawingfragment.hxx" @@ -389,6 +390,8 @@ public: inline CondFormatBuffer& getCondFormats() { return maCondFormats; } /** Returns the buffer for all cell comments in this sheet. */ inline CommentsBuffer& getComments() { return maComments; } + /** Returns the auto filters for the sheet. */ + inline AutoFilterBuffer& getAutoFilters() { return maAutoFilters; } /** Returns the buffer for all web query tables in this sheet. */ inline QueryTableBuffer& getQueryTables() { return maQueryTables; } /** Returns the page/print settings for this sheet. */ @@ -560,6 +563,7 @@ private: SharedFormulaBuffer maSharedFmlas; /// Buffer for shared formulas in this sheet. CondFormatBuffer maCondFormats; /// Buffer for conditional formattings. CommentsBuffer maComments; /// Buffer for all cell comments in this sheet. + AutoFilterBuffer maAutoFilters; /// Sheet auto filters (not associated to a table). QueryTableBuffer maQueryTables; /// Buffer for all web query tables in this sheet. PageSettings maPageSett; /// Page/print settings for this sheet. SheetViewSettings maSheetViewSett; /// View settings for this sheet. @@ -589,6 +593,7 @@ WorksheetData::WorksheetData( const WorkbookHelper& rHelper, const ISegmentProgr maSharedFmlas( *this ), maCondFormats( *this ), maComments( *this ), + maAutoFilters( *this ), maQueryTables( *this ), maPageSett( *this ), maSheetViewSett( *this ), @@ -1050,6 +1055,7 @@ void WorksheetData::finalizeWorksheetImport() finalizeHyperlinkRanges(); finalizeValidationRanges(); finalizeMergedRanges(); + maAutoFilters.finalizeImport( getSheetIndex() ); maSheetSett.finalizeImport(); maCondFormats.finalizeImport(); maQueryTables.finalizeImport(); @@ -1842,6 +1848,11 @@ CommentsBuffer& WorksheetHelper::getComments() const return mrSheetData.getComments(); } +AutoFilterBuffer& WorksheetHelper::getAutoFilters() const +{ + return mrSheetData.getAutoFilters(); +} + QueryTableBuffer& WorksheetHelper::getQueryTables() const { return mrSheetData.getQueryTables(); |