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