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