LCOV - code coverage report
Current view: top level - sc/source/filter/oox - autofilterbuffer.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 161 346 46.5 %
Date: 2014-11-03 Functions: 26 44 59.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include "autofilterbuffer.hxx"
      21             : 
      22             : #include <com/sun/star/sheet/FilterConnection.hpp>
      23             : #include <com/sun/star/sheet/FilterOperator2.hpp>
      24             : #include <com/sun/star/sheet/TableFilterField3.hpp>
      25             : #include <com/sun/star/sheet/XDatabaseRange.hpp>
      26             : #include <com/sun/star/sheet/XSheetFilterDescriptor3.hpp>
      27             : #include <com/sun/star/table/TableOrientation.hpp>
      28             : #include <rtl/ustrbuf.hxx>
      29             : #include <oox/helper/attributelist.hxx>
      30             : #include <oox/helper/containerhelper.hxx>
      31             : #include <oox/helper/propertyset.hxx>
      32             : #include <oox/token/properties.hxx>
      33             : #include "addressconverter.hxx"
      34             : #include "biffinputstream.hxx"
      35             : #include "defnamesbuffer.hxx"
      36             : 
      37             : namespace oox {
      38             : namespace xls {
      39             : 
      40             : using namespace ::com::sun::star::sheet;
      41             : using namespace ::com::sun::star::table;
      42             : using namespace ::com::sun::star::uno;
      43             : 
      44             : namespace {
      45             : 
      46             : const sal_uInt8 BIFF12_TOP10FILTER_TOP              = 0x01;
      47             : const sal_uInt8 BIFF12_TOP10FILTER_PERCENT          = 0x02;
      48             : 
      49             : const sal_uInt16 BIFF12_FILTERCOLUMN_HIDDENBUTTON   = 0x0001;
      50             : const sal_uInt16 BIFF12_FILTERCOLUMN_SHOWBUTTON     = 0x0002;
      51             : 
      52             : const sal_uInt8 BIFF_FILTER_DATATYPE_NONE           = 0;
      53             : const sal_uInt8 BIFF_FILTER_DATATYPE_DOUBLE         = 4;
      54             : const sal_uInt8 BIFF_FILTER_DATATYPE_STRING         = 6;
      55             : const sal_uInt8 BIFF_FILTER_DATATYPE_BOOLEAN        = 8;
      56             : const sal_uInt8 BIFF_FILTER_DATATYPE_EMPTY          = 12;
      57             : const sal_uInt8 BIFF_FILTER_DATATYPE_NOTEMPTY       = 14;
      58             : 
      59           2 : bool lclGetApiOperatorFromToken( sal_Int32& rnApiOperator, sal_Int32 nToken )
      60             : {
      61           2 :     switch( nToken )
      62             :     {
      63           0 :         case XML_lessThan:              rnApiOperator = FilterOperator2::NOT_EQUAL;     return true;
      64           2 :         case XML_equal:                 rnApiOperator = FilterOperator2::EQUAL;         return true;
      65           0 :         case XML_lessThanOrEqual:       rnApiOperator = FilterOperator2::LESS_EQUAL;    return true;
      66           0 :         case XML_greaterThan:           rnApiOperator = FilterOperator2::GREATER;       return true;
      67           0 :         case XML_notEqual:              rnApiOperator = FilterOperator2::NOT_EQUAL;     return true;
      68           0 :         case XML_greaterThanOrEqual:    rnApiOperator = FilterOperator2::GREATER_EQUAL; return true;
      69             :     }
      70           0 :     return false;
      71             : }
      72             : 
      73             : /** Removes leading asterisk characters from the passed string.
      74             :     @return  True = at least one asterisk character has been removed. */
      75           2 : bool lclTrimLeadingAsterisks( OUString& rValue )
      76             : {
      77           2 :     sal_Int32 nLength = rValue.getLength();
      78           2 :     sal_Int32 nPos = 0;
      79           4 :     while( (nPos < nLength) && (rValue[ nPos ] == '*') )
      80           0 :         ++nPos;
      81           2 :     if( nPos > 0 )
      82             :     {
      83           0 :         rValue = rValue.copy( nPos );
      84           0 :         return true;
      85             :     }
      86           2 :     return false;
      87             : }
      88             : 
      89             : /** Removes trailing asterisk characters from the passed string.
      90             :     @return  True = at least one asterisk character has been removed. */
      91           2 : bool lclTrimTrailingAsterisks( OUString& rValue )
      92             : {
      93           2 :     sal_Int32 nLength = rValue.getLength();
      94           2 :     sal_Int32 nPos = nLength;
      95           4 :     while( (nPos > 0) && (rValue[ nPos - 1 ] == '*') )
      96           0 :         --nPos;
      97           2 :     if( nPos < nLength )
      98             :     {
      99           0 :         rValue = rValue.copy( 0, nPos );
     100           0 :         return true;
     101             :     }
     102           2 :     return false;
     103             : }
     104             : 
     105             : /** Converts wildcard characters '*' and '?' to regular expressions and quotes
     106             :     RE meta characters.
     107             :     @return  True = passed string has been changed (RE needs to be enabled). */
     108           2 : bool lclConvertWildcardsToRegExp( OUString& rValue )
     109             : {
     110             :     // check existence of the wildcard characters '*' and '?'
     111           2 :     if( !rValue.isEmpty() && ((rValue.indexOf( '*' ) >= 0) || (rValue.indexOf( '?' ) >= 0)) )
     112             :     {
     113           0 :         OUStringBuffer aBuffer;
     114           0 :         aBuffer.ensureCapacity( rValue.getLength() + 5 );
     115           0 :         const sal_Unicode* pcChar = rValue.getStr();
     116           0 :         const sal_Unicode* pcEnd = pcChar + rValue.getLength();
     117           0 :         for( ; pcChar < pcEnd; ++pcChar )
     118             :         {
     119           0 :             switch( *pcChar )
     120             :             {
     121             :                 case '?':
     122           0 :                     aBuffer.append( '.' );
     123           0 :                 break;
     124             :                 case '*':
     125           0 :                     aBuffer.append( '.' ).append( '*' );
     126           0 :                 break;
     127             :                 case '\\': case '.': case '|': case '(': case ')': case '^': case '$':
     128             :                     // quote RE meta characters
     129           0 :                     aBuffer.append( '\\' ).append( *pcChar );
     130           0 :                 break;
     131             :                 default:
     132           0 :                     aBuffer.append( *pcChar );
     133             :             }
     134             :         }
     135           0 :         rValue = aBuffer.makeStringAndClear();
     136           0 :         return true;
     137             :     }
     138           2 :     return false;
     139             : }
     140             : 
     141             : } // namespace
     142             : 
     143           4 : ApiFilterSettings::ApiFilterSettings()
     144             : {
     145           4 : }
     146             : 
     147           0 : void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, double fValue )
     148             : {
     149           0 :     maFilterFields.resize( maFilterFields.size() + 1 );
     150           0 :     TableFilterField3& rFilterField = maFilterFields.back();
     151           0 :     rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
     152           0 :     rFilterField.Operator = nOperator;
     153           0 :     rFilterField.Values.realloc(1);
     154           0 :     rFilterField.Values[0].IsNumeric = true;
     155           0 :     rFilterField.Values[0].NumericValue = fValue;
     156           0 : }
     157             : 
     158           2 : void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, const OUString& rValue )
     159             : {
     160           2 :     maFilterFields.resize( maFilterFields.size() + 1 );
     161           2 :     TableFilterField3& rFilterField = maFilterFields.back();
     162           2 :     rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
     163           2 :     rFilterField.Operator = nOperator;
     164           2 :     rFilterField.Values.realloc(1);
     165           2 :     rFilterField.Values[0].IsNumeric = false;
     166           2 :     rFilterField.Values[0].StringValue = rValue;
     167           2 : }
     168             : 
     169           0 : void ApiFilterSettings::appendField( bool bAnd, const std::vector<OUString>& rValues )
     170             : {
     171           0 :     maFilterFields.resize( maFilterFields.size() + 1 );
     172           0 :     TableFilterField3& rFilterField = maFilterFields.back();
     173           0 :     rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
     174           0 :     rFilterField.Operator = FilterOperator2::EQUAL;
     175           0 :     size_t n = rValues.size();
     176           0 :     rFilterField.Values.realloc(n);
     177           0 :     for (size_t i = 0; i < n; ++i)
     178             :     {
     179           0 :         rFilterField.Values[i].IsNumeric = false;
     180           0 :         rFilterField.Values[i].StringValue = rValues[i];
     181             :     }
     182           0 : }
     183             : 
     184           2 : FilterSettingsBase::FilterSettingsBase( const WorkbookHelper& rHelper ) :
     185           2 :     WorkbookHelper( rHelper )
     186             : {
     187           2 : }
     188             : 
     189           0 : void FilterSettingsBase::importAttribs( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
     190             : {
     191           0 : }
     192             : 
     193           0 : void FilterSettingsBase::importRecord( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ )
     194             : {
     195           0 : }
     196             : 
     197           0 : ApiFilterSettings FilterSettingsBase::finalizeImport( sal_Int32 /*nMaxCount*/ )
     198             : {
     199           0 :     return ApiFilterSettings();
     200             : }
     201             : 
     202           0 : DiscreteFilter::DiscreteFilter( const WorkbookHelper& rHelper ) :
     203             :     FilterSettingsBase( rHelper ),
     204             :     mnCalendarType( XML_none ),
     205           0 :     mbShowBlank( false )
     206             : {
     207           0 : }
     208             : 
     209           0 : void DiscreteFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
     210             : {
     211           0 :     switch( nElement )
     212             :     {
     213             :         case XLS_TOKEN( filters ):
     214           0 :             mnCalendarType = rAttribs.getToken( XML_calendarType, XML_none );
     215           0 :             mbShowBlank = rAttribs.getBool( XML_blank, false );
     216           0 :         break;
     217             : 
     218             :         case XLS_TOKEN( filter ):
     219             :         {
     220           0 :             OUString aValue = rAttribs.getXString( XML_val, OUString() );
     221           0 :             if( !aValue.isEmpty() )
     222           0 :                 maValues.push_back( aValue );
     223             :         }
     224           0 :         break;
     225             :     }
     226           0 : }
     227             : 
     228           0 : void DiscreteFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
     229             : {
     230           0 :     switch( nRecId )
     231             :     {
     232             :         case BIFF12_ID_DISCRETEFILTERS:
     233             :         {
     234             :             sal_Int32 nShowBlank, nCalendarType;
     235           0 :             rStrm >> nShowBlank >> nCalendarType;
     236             : 
     237             :             static const sal_Int32 spnCalendarTypes[] = {
     238             :                 XML_none, XML_gregorian, XML_gregorianUs, XML_japan, XML_taiwan, XML_korea, XML_hijri, XML_thai, XML_hebrew,
     239             :                 XML_gregorianMeFrench, XML_gregorianArabic, XML_gregorianXlitEnglish, XML_gregorianXlitFrench };
     240           0 :             mnCalendarType = STATIC_ARRAY_SELECT( spnCalendarTypes, nCalendarType, XML_none );
     241           0 :             mbShowBlank = nShowBlank != 0;
     242             :         }
     243           0 :         break;
     244             : 
     245             :         case BIFF12_ID_DISCRETEFILTER:
     246             :         {
     247           0 :             OUString aValue = BiffHelper::readString( rStrm );
     248           0 :             if( !aValue.isEmpty() )
     249           0 :                 maValues.push_back( aValue );
     250             :         }
     251           0 :         break;
     252             :     }
     253           0 : }
     254             : 
     255           0 : ApiFilterSettings DiscreteFilter::finalizeImport( sal_Int32 nMaxCount )
     256             : {
     257           0 :     ApiFilterSettings aSettings;
     258           0 :     if( static_cast< sal_Int32 >( maValues.size() ) <= nMaxCount )
     259             :     {
     260           0 :         aSettings.maFilterFields.reserve( maValues.size() );
     261             : 
     262             :         // insert all filter values
     263           0 :         aSettings.appendField( true, maValues );
     264             : 
     265             :         // extra field for 'show empty'
     266           0 :         if( mbShowBlank )
     267           0 :             aSettings.appendField( false, FilterOperator2::EMPTY, OUString() );
     268             : 
     269             :         /*  Require disabled regular expressions, filter entries may contain
     270             :             any RE meta characters. */
     271           0 :         if( !maValues.empty() )
     272           0 :             aSettings.mobNeedsRegExp = false;
     273             :     }
     274           0 :     return aSettings;
     275             : }
     276             : 
     277           0 : Top10Filter::Top10Filter( const WorkbookHelper& rHelper ) :
     278             :     FilterSettingsBase( rHelper ),
     279             :     mfValue( 0.0 ),
     280             :     mbTop( true ),
     281           0 :     mbPercent( false )
     282             : {
     283           0 : }
     284             : 
     285           0 : void Top10Filter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
     286             : {
     287           0 :     if( nElement == XLS_TOKEN( top10 ) )
     288             :     {
     289           0 :         mfValue = rAttribs.getDouble( XML_val, 0.0 );
     290           0 :         mbTop = rAttribs.getBool( XML_top, true );
     291           0 :         mbPercent = rAttribs.getBool( XML_percent, false );
     292             :     }
     293           0 : }
     294             : 
     295           0 : void Top10Filter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
     296             : {
     297           0 :     if( nRecId == BIFF12_ID_TOP10FILTER )
     298             :     {
     299             :         sal_uInt8 nFlags;
     300           0 :         rStrm >> nFlags >> mfValue;
     301           0 :         mbTop = getFlag( nFlags, BIFF12_TOP10FILTER_TOP );
     302           0 :         mbPercent = getFlag( nFlags, BIFF12_TOP10FILTER_PERCENT );
     303             :     }
     304           0 : }
     305             : 
     306           0 : ApiFilterSettings Top10Filter::finalizeImport( sal_Int32 /*nMaxCount*/ )
     307             : {
     308             :     sal_Int32 nOperator = mbTop ?
     309             :         (mbPercent ? FilterOperator2::TOP_PERCENT : FilterOperator2::TOP_VALUES) :
     310           0 :         (mbPercent ? FilterOperator2::BOTTOM_PERCENT : FilterOperator2::BOTTOM_VALUES);
     311           0 :     ApiFilterSettings aSettings;
     312           0 :     aSettings.appendField( true, nOperator, mfValue );
     313           0 :     return aSettings;
     314             : }
     315             : 
     316           2 : FilterCriterionModel::FilterCriterionModel() :
     317             :     mnOperator( XML_equal ),
     318             :     mnDataType( BIFF_FILTER_DATATYPE_NONE ),
     319           2 :     mnStrLen( 0 )
     320             : {
     321           2 : }
     322             : 
     323           0 : void FilterCriterionModel::setBiffOperator( sal_uInt8 nOperator )
     324             : {
     325             :     static const sal_Int32 spnOperators[] = { XML_TOKEN_INVALID,
     326             :         XML_lessThan, XML_equal, XML_lessThanOrEqual, XML_greaterThan, XML_notEqual, XML_greaterThanOrEqual };
     327           0 :     mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
     328           0 : }
     329             : 
     330           0 : void FilterCriterionModel::readBiffData( SequenceInputStream& rStrm )
     331             : {
     332             :     sal_uInt8 nOperator;
     333           0 :     rStrm >> mnDataType >> nOperator;
     334           0 :     setBiffOperator( nOperator );
     335             : 
     336           0 :     switch( mnDataType )
     337             :     {
     338             :         case BIFF_FILTER_DATATYPE_DOUBLE:
     339           0 :             maValue <<= rStrm.readDouble();
     340           0 :         break;
     341             :         case BIFF_FILTER_DATATYPE_STRING:
     342             :         {
     343           0 :             rStrm.skip( 8 );
     344           0 :             OUString aValue = BiffHelper::readString( rStrm ).trim();
     345           0 :             if( !aValue.isEmpty() )
     346           0 :                 maValue <<= aValue;
     347             :         }
     348           0 :         break;
     349             :         case BIFF_FILTER_DATATYPE_BOOLEAN:
     350           0 :             maValue <<= (rStrm.readuInt8() != 0);
     351           0 :             rStrm.skip( 7 );
     352           0 :         break;
     353             :         case BIFF_FILTER_DATATYPE_EMPTY:
     354           0 :             rStrm.skip( 8 );
     355           0 :             if( mnOperator == XML_equal )
     356           0 :                 maValue <<= OUString();
     357           0 :         break;
     358             :         case BIFF_FILTER_DATATYPE_NOTEMPTY:
     359           0 :             rStrm.skip( 8 );
     360           0 :             if( mnOperator == XML_notEqual )
     361           0 :                 maValue <<= OUString();
     362           0 :         break;
     363             :         default:
     364             :             OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" );
     365           0 :             rStrm.skip( 8 );
     366             :     }
     367           0 : }
     368             : 
     369           2 : CustomFilter::CustomFilter( const WorkbookHelper& rHelper ) :
     370             :     FilterSettingsBase( rHelper ),
     371           2 :     mbAnd( false )
     372             : {
     373           2 : }
     374             : 
     375           4 : void CustomFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
     376             : {
     377           4 :     switch( nElement )
     378             :     {
     379             :         case XLS_TOKEN( customFilters ):
     380           2 :             mbAnd = rAttribs.getBool( XML_and, false );
     381           2 :         break;
     382             : 
     383             :         case XLS_TOKEN( customFilter ):
     384             :         {
     385           2 :             FilterCriterionModel aCriterion;
     386           2 :             aCriterion.mnOperator = rAttribs.getToken( XML_operator, XML_equal );
     387           4 :             OUString aValue = rAttribs.getXString( XML_val, OUString() ).trim();
     388           2 :             if( (aCriterion.mnOperator == XML_equal) || (aCriterion.mnOperator == XML_notEqual) || (!aValue.isEmpty()) )
     389           2 :                 aCriterion.maValue <<= aValue;
     390           4 :             appendCriterion( aCriterion );
     391             :         }
     392           2 :         break;
     393             :     }
     394           4 : }
     395             : 
     396           0 : void CustomFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
     397             : {
     398           0 :     switch( nRecId )
     399             :     {
     400             :         case BIFF12_ID_CUSTOMFILTERS:
     401           0 :             mbAnd = rStrm.readInt32() == 0;
     402           0 :         break;
     403             : 
     404             :         case BIFF12_ID_CUSTOMFILTER:
     405             :         {
     406           0 :             FilterCriterionModel aCriterion;
     407           0 :             aCriterion.readBiffData( rStrm );
     408           0 :             appendCriterion( aCriterion );
     409             :         }
     410           0 :         break;
     411             :     }
     412           0 : }
     413             : 
     414           2 : ApiFilterSettings CustomFilter::finalizeImport( sal_Int32 /*nMaxCount*/ )
     415             : {
     416           2 :     ApiFilterSettings aSettings;
     417             :     OSL_ENSURE( maCriteria.size() <= 2, "CustomFilter::finalizeImport - too many filter criteria" );
     418           4 :     for( FilterCriterionVector::iterator aIt = maCriteria.begin(), aEnd = maCriteria.end(); aIt != aEnd; ++aIt )
     419             :     {
     420             :         // first extract the filter operator
     421           2 :         sal_Int32 nOperator = 0;
     422           2 :         bool bValidOperator = lclGetApiOperatorFromToken( nOperator, aIt->mnOperator );
     423           2 :         if( bValidOperator )
     424             :         {
     425           2 :             if( aIt->maValue.has< OUString >() )
     426             :             {
     427             :                 // string argument
     428           2 :                 OUString aValue;
     429           2 :                 aIt->maValue >>= aValue;
     430             :                 // check for 'empty', 'contains', 'begins with', or 'ends with' text filters
     431           2 :                 bool bEqual = nOperator == FilterOperator2::EQUAL;
     432           2 :                 bool bNotEqual = nOperator == FilterOperator2::NOT_EQUAL;
     433           2 :                 if( bEqual || bNotEqual )
     434             :                 {
     435           2 :                     if( aValue.isEmpty() )
     436             :                     {
     437             :                         // empty comparison string: create empty/not empty filters
     438           0 :                         nOperator = bNotEqual ? FilterOperator2::NOT_EMPTY : FilterOperator2::EMPTY;
     439             :                     }
     440             :                     else
     441             :                     {
     442             :                         // compare to something: try to find begins/ends/contains
     443           2 :                         bool bHasLeadingAsterisk = lclTrimLeadingAsterisks( aValue );
     444           2 :                         bool bHasTrailingAsterisk = lclTrimTrailingAsterisks( aValue );
     445             :                         // just '***' matches everything, do not create a filter field
     446           2 :                         bValidOperator = !aValue.isEmpty();
     447           2 :                         if( bValidOperator )
     448             :                         {
     449           2 :                             if( bHasLeadingAsterisk && bHasTrailingAsterisk )
     450           0 :                                 nOperator = bNotEqual ? FilterOperator2::DOES_NOT_CONTAIN : FilterOperator2::CONTAINS;
     451           2 :                             else if( bHasLeadingAsterisk )
     452           0 :                                 nOperator = bNotEqual ? FilterOperator2::DOES_NOT_END_WITH : FilterOperator2::ENDS_WITH;
     453           2 :                             else if( bHasTrailingAsterisk )
     454           0 :                                 nOperator = bNotEqual ? FilterOperator2::DOES_NOT_BEGIN_WITH : FilterOperator2::BEGINS_WITH;
     455             :                             // else: no asterisks, stick to equal/not equal
     456             :                         }
     457             :                     }
     458             :                 }
     459             : 
     460           2 :                 if( bValidOperator )
     461             :                 {
     462             :                     // if wildcards are present, require RE mode, otherwise keep don't care state
     463           2 :                     if( lclConvertWildcardsToRegExp( aValue ) )
     464           0 :                         aSettings.mobNeedsRegExp = true;
     465             :                     // create a new UNO API filter field
     466           2 :                     aSettings.appendField( mbAnd, nOperator, aValue );
     467           2 :                 }
     468             :             }
     469           0 :             else if( aIt->maValue.has< double >() )
     470             :             {
     471             :                 // floating-point argument
     472           0 :                 double fValue = 0.0;
     473           0 :                 aIt->maValue >>= fValue;
     474           0 :                 aSettings.appendField( mbAnd, nOperator, fValue );
     475             :             }
     476             :         }
     477             :     }
     478           2 :     return aSettings;
     479             : }
     480             : 
     481           2 : void CustomFilter::appendCriterion( const FilterCriterionModel& rCriterion )
     482             : {
     483           2 :     if( (rCriterion.mnOperator != XML_TOKEN_INVALID) && rCriterion.maValue.hasValue() )
     484           2 :         maCriteria.push_back( rCriterion );
     485           2 : }
     486             : 
     487           2 : FilterColumn::FilterColumn( const WorkbookHelper& rHelper ) :
     488             :     WorkbookHelper( rHelper ),
     489             :     mnColId( -1 ),
     490             :     mbHiddenButton( false ),
     491           2 :     mbShowButton( true )
     492             : {
     493           2 : }
     494             : 
     495           2 : void FilterColumn::importFilterColumn( const AttributeList& rAttribs )
     496             : {
     497           2 :     mnColId = rAttribs.getInteger( XML_colId, -1 );
     498           2 :     mbHiddenButton = rAttribs.getBool( XML_hiddenButton, false );
     499           2 :     mbShowButton = rAttribs.getBool( XML_showButton, true );
     500           2 : }
     501             : 
     502           0 : void FilterColumn::importFilterColumn( SequenceInputStream& rStrm )
     503             : {
     504             :     sal_uInt16 nFlags;
     505           0 :     rStrm >> mnColId >> nFlags;
     506           0 :     mbHiddenButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_HIDDENBUTTON );
     507           0 :     mbShowButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_SHOWBUTTON );
     508           0 : }
     509             : 
     510           2 : ApiFilterSettings FilterColumn::finalizeImport( sal_Int32 nMaxCount )
     511             : {
     512           2 :     ApiFilterSettings aSettings;
     513           2 :     if( (0 <= mnColId) && mxSettings.get() )
     514             :     {
     515             :         // filter settings object creates a sequence of filter fields
     516           2 :         aSettings = mxSettings->finalizeImport( nMaxCount );
     517             :         // add column index to all filter fields
     518           4 :         for( ApiFilterSettings::FilterFieldVector::iterator aIt = aSettings.maFilterFields.begin(), aEnd = aSettings.maFilterFields.end(); aIt != aEnd; ++aIt )
     519           2 :             aIt->Field = mnColId;
     520             :     }
     521           2 :     return aSettings;
     522             : }
     523             : 
     524           2 : AutoFilter::AutoFilter( const WorkbookHelper& rHelper ) :
     525           2 :     WorkbookHelper( rHelper )
     526             : {
     527           2 : }
     528             : 
     529           2 : void AutoFilter::importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet )
     530             : {
     531           2 :     OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
     532           2 :     getAddressConverter().convertToCellRangeUnchecked( maRange, aRangeStr, nSheet );
     533           2 : }
     534             : 
     535           0 : void AutoFilter::importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet )
     536             : {
     537           0 :     BinRange aBinRange;
     538           0 :     rStrm >> aBinRange;
     539           0 :     getAddressConverter().convertToCellRangeUnchecked( maRange, aBinRange, nSheet );
     540           0 : }
     541             : 
     542           2 : FilterColumn& AutoFilter::createFilterColumn()
     543             : {
     544           2 :     FilterColumnVector::value_type xFilterColumn( new FilterColumn( *this ) );
     545           2 :     maFilterColumns.push_back( xFilterColumn );
     546           2 :     return *xFilterColumn;
     547             : }
     548             : 
     549           2 : void AutoFilter::finalizeImport( const Reference<XSheetFilterDescriptor3>& rxFilterDesc )
     550             : {
     551           2 :     if( rxFilterDesc.is() )
     552             :     {
     553             :         // set some common properties for the auto filter range
     554           2 :         PropertySet aDescProps( rxFilterDesc );
     555           2 :         aDescProps.setProperty( PROP_IsCaseSensitive, false );
     556           2 :         aDescProps.setProperty( PROP_SkipDuplicates, false );
     557           2 :         aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
     558           2 :         aDescProps.setProperty( PROP_ContainsHeader, true );
     559           2 :         aDescProps.setProperty( PROP_CopyOutputData, false );
     560             : 
     561             :         // maximum number of UNO API filter fields
     562           2 :         sal_Int32 nMaxCount = 0;
     563           2 :         aDescProps.getProperty( nMaxCount, PROP_MaxFieldCount );
     564             :         OSL_ENSURE( nMaxCount > 0, "AutoFilter::finalizeImport - invalid maximum filter field count" );
     565             : 
     566             :         // resulting list of all UNO API filter fields
     567           4 :         ::std::vector<TableFilterField3> aFilterFields;
     568             : 
     569             :         // track if columns require to enable or disable regular expressions
     570           2 :         OptValue< bool > obNeedsRegExp;
     571             : 
     572             :         /*  Track whether the filter fields of the first filter column are
     573             :             connected with 'or'. In this case, other filter fields cannot be
     574             :             inserted without altering the result of the entire filter, due to
     575             :             Calc's precedence for the 'and' connection operator. Example:
     576             :             Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and
     577             :             B2 belong to filter column B, will be evaluated by Calc as
     578             :             '(A1 and B1) or (B2 and C1)'. */
     579           2 :         bool bHasOrConnection = false;
     580             : 
     581             :         // process all filter column objects, exit when 'or' connection exists
     582           4 :         for( FilterColumnVector::iterator aIt = maFilterColumns.begin(), aEnd = maFilterColumns.end(); !bHasOrConnection && (aIt != aEnd); ++aIt )
     583             :         {
     584             :             // the filter settings object creates a list of filter fields
     585           2 :             ApiFilterSettings aSettings = (*aIt)->finalizeImport( nMaxCount );
     586           2 :             ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields;
     587             : 
     588             :             // new total number of filter fields
     589           2 :             sal_Int32 nNewCount = static_cast< sal_Int32 >( aFilterFields.size() + rColumnFields.size() );
     590             : 
     591             :             /*  Check whether mode for regular expressions is compatible with
     592             :                 the global mode in obNeedsRegExp. If either one is still in
     593             :                 don't-care state, all is fine. If both are set, they must be
     594             :                 equal. */
     595           2 :             bool bRegExpCompatible = !obNeedsRegExp || !aSettings.mobNeedsRegExp || (obNeedsRegExp.get() == aSettings.mobNeedsRegExp.get());
     596             : 
     597             :             // check whether fields are connected by 'or' (see comments above).
     598           2 :             if( rColumnFields.size() >= 2 )
     599           0 :                 for( ApiFilterSettings::FilterFieldVector::iterator aSIt = rColumnFields.begin() + 1, aSEnd = rColumnFields.end(); !bHasOrConnection && (aSIt != aSEnd); ++aSIt )
     600           0 :                     bHasOrConnection = aSIt->Connection == FilterConnection_OR;
     601             : 
     602             :             /*  Skip the column filter, if no filter fields have been created,
     603             :                 if the number of new filter fields would exceed the total limit
     604             :                 of filter fields, or if the mode for regular expressions of the
     605             :                 filter column does not fit. */
     606           2 :             if( !rColumnFields.empty() && (nNewCount <= nMaxCount) && bRegExpCompatible )
     607             :             {
     608             :                 /*  Add 'and' connection to the first filter field to connect
     609             :                     it to the existing filter fields of other columns. */
     610           2 :                 rColumnFields[ 0 ].Connection = FilterConnection_AND;
     611             : 
     612             :                 // insert the new filter fields
     613           2 :                 aFilterFields.insert( aFilterFields.end(), rColumnFields.begin(), rColumnFields.end() );
     614             : 
     615             :                 // update the regular expressions mode
     616           2 :                 obNeedsRegExp.assignIfUsed( aSettings.mobNeedsRegExp );
     617             :             }
     618           2 :         }
     619             : 
     620             :         // insert all filter fields to the filter descriptor
     621           2 :         if( !aFilterFields.empty() )
     622           2 :             rxFilterDesc->setFilterFields3( ContainerHelper::vectorToSequence( aFilterFields ) );
     623             : 
     624             :         // regular expressions
     625           2 :         bool bUseRegExp = obNeedsRegExp.get( false );
     626           4 :         aDescProps.setProperty( PROP_UseRegularExpressions, bUseRegExp );
     627             :     }
     628           2 : }
     629             : 
     630         260 : AutoFilterBuffer::AutoFilterBuffer( const WorkbookHelper& rHelper ) :
     631         260 :     WorkbookHelper( rHelper )
     632             : {
     633         260 : }
     634             : 
     635           2 : AutoFilter& AutoFilterBuffer::createAutoFilter()
     636             : {
     637           2 :     AutoFilterVector::value_type xAutoFilter( new AutoFilter( *this ) );
     638           2 :     maAutoFilters.push_back( xAutoFilter );
     639           2 :     return *xAutoFilter;
     640             : }
     641             : 
     642         260 : void AutoFilterBuffer::finalizeImport( sal_Int16 nSheet )
     643             : {
     644             :     // rely on existence of the defined name '_FilterDatabase' containing the range address of the filtered area
     645         260 :     if( const DefinedName* pFilterDBName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_FILTERDATABASE, nSheet ).get() )
     646             :     {
     647           2 :         CellRangeAddress aFilterRange;
     648           2 :         if( pFilterDBName->getAbsoluteRange( aFilterRange ) && (aFilterRange.Sheet == nSheet) )
     649             :         {
     650             :             // use the same name for the database range as used for the defined name '_FilterDatabase'
     651           2 :             Reference< XDatabaseRange > xDatabaseRange = createUnnamedDatabaseRangeObject( aFilterRange );
     652             :             // first, try to create an auto filter
     653           2 :             bool bHasAutoFilter = finalizeImport( xDatabaseRange );
     654             :             // no success: try to create an advanced filter
     655           2 :             if( !bHasAutoFilter && xDatabaseRange.is() )
     656             :             {
     657             :                 // the built-in defined name 'Criteria' must exist
     658           0 :                 if( const DefinedName* pCriteriaName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_CRITERIA, nSheet ).get() )
     659             :                 {
     660           0 :                     CellRangeAddress aCriteriaRange;
     661           0 :                     if( pCriteriaName->getAbsoluteRange( aCriteriaRange ) )
     662             :                     {
     663             :                         // set some common properties for the filter descriptor
     664           0 :                         PropertySet aDescProps( xDatabaseRange->getFilterDescriptor() );
     665           0 :                         aDescProps.setProperty( PROP_IsCaseSensitive, false );
     666           0 :                         aDescProps.setProperty( PROP_SkipDuplicates, false );
     667           0 :                         aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
     668           0 :                         aDescProps.setProperty( PROP_ContainsHeader, true );
     669             :                         // criteria range may contain wildcards, but these are incompatible with REs
     670           0 :                         aDescProps.setProperty( PROP_UseRegularExpressions, false );
     671             : 
     672             :                         // position of output data (if built-in defined name 'Extract' exists)
     673           0 :                         DefinedNameRef xExtractName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_EXTRACT, nSheet );
     674           0 :                         CellRangeAddress aOutputRange;
     675           0 :                         bool bHasOutputRange = xExtractName.get() && xExtractName->getAbsoluteRange( aOutputRange );
     676           0 :                         aDescProps.setProperty( PROP_CopyOutputData, bHasOutputRange );
     677           0 :                         if( bHasOutputRange )
     678             :                         {
     679           0 :                             aDescProps.setProperty( PROP_SaveOutputPosition, true );
     680           0 :                             aDescProps.setProperty( PROP_OutputPosition, CellAddress( aOutputRange.Sheet, aOutputRange.StartColumn, aOutputRange.StartRow ) );
     681             :                         }
     682             : 
     683             :                         /*  Properties of the database range (must be set after
     684             :                             modifying properties of the filter descriptor,
     685             :                             otherwise the 'FilterCriteriaSource' property gets
     686             :                             deleted). */
     687           0 :                         PropertySet aRangeProps( xDatabaseRange );
     688           0 :                         aRangeProps.setProperty( PROP_AutoFilter, false );
     689           0 :                         aRangeProps.setProperty( PROP_FilterCriteriaSource, aCriteriaRange );
     690             :                     }
     691             :                 }
     692           2 :             }
     693             :         }
     694             :     }
     695         260 : }
     696             : 
     697           2 : bool AutoFilterBuffer::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRange )
     698             : {
     699           2 :     AutoFilter* pAutoFilter = getActiveAutoFilter();
     700           2 :     if( pAutoFilter && rxDatabaseRange.is() ) try
     701             :     {
     702             :         // the property 'AutoFilter' enables the drop-down buttons
     703           2 :         PropertySet aRangeProps( rxDatabaseRange );
     704           2 :         aRangeProps.setProperty( PROP_AutoFilter, true );
     705             :         // convert filter settings using the filter descriptor of the database range
     706           4 :         Reference< XSheetFilterDescriptor3 > xFilterDesc( rxDatabaseRange->getFilterDescriptor(), UNO_QUERY_THROW );
     707           2 :         pAutoFilter->finalizeImport( xFilterDesc );
     708             :         // return true to indicate enabled autofilter
     709           4 :         return true;
     710             :     }
     711           0 :     catch( Exception& )
     712             :     {
     713             :     }
     714           0 :     return false;
     715             : }
     716             : 
     717           2 : AutoFilter* AutoFilterBuffer::getActiveAutoFilter()
     718             : {
     719             :     // Excel expects not more than one auto filter per sheet or table
     720             :     OSL_ENSURE( maAutoFilters.size() <= 1, "AutoFilterBuffer::getActiveAutoFilter - too many auto filters" );
     721             :     // stick to the last imported auto filter
     722           2 :     return maAutoFilters.empty() ? 0 : maAutoFilters.back().get();
     723             : }
     724             : 
     725             : } // namespace xls
     726          48 : } // namespace oox
     727             : 
     728             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10