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: */
|