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