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 <osl/diagnose.h>
30 : #include <oox/helper/attributelist.hxx>
31 : #include <oox/helper/containerhelper.hxx>
32 : #include <oox/helper/propertyset.hxx>
33 : #include <oox/token/properties.hxx>
34 : #include "addressconverter.hxx"
35 : #include "biffinputstream.hxx"
36 : #include "defnamesbuffer.hxx"
37 :
38 : namespace oox {
39 : namespace xls {
40 :
41 : using namespace ::com::sun::star::sheet;
42 : using namespace ::com::sun::star::table;
43 : using namespace ::com::sun::star::uno;
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 : nShowBlank = rStrm.readInt32();
237 0 : nCalendarType = rStrm.readInt32();
238 :
239 : static const sal_Int32 spnCalendarTypes[] = {
240 : XML_none, XML_gregorian, XML_gregorianUs, XML_japan, XML_taiwan, XML_korea, XML_hijri, XML_thai, XML_hebrew,
241 : XML_gregorianMeFrench, XML_gregorianArabic, XML_gregorianXlitEnglish, XML_gregorianXlitFrench };
242 0 : mnCalendarType = STATIC_ARRAY_SELECT( spnCalendarTypes, nCalendarType, XML_none );
243 0 : mbShowBlank = nShowBlank != 0;
244 : }
245 0 : break;
246 :
247 : case BIFF12_ID_DISCRETEFILTER:
248 : {
249 0 : OUString aValue = BiffHelper::readString( rStrm );
250 0 : if( !aValue.isEmpty() )
251 0 : maValues.push_back( aValue );
252 : }
253 0 : break;
254 : }
255 0 : }
256 :
257 0 : ApiFilterSettings DiscreteFilter::finalizeImport( sal_Int32 nMaxCount )
258 : {
259 0 : ApiFilterSettings aSettings;
260 0 : if( static_cast< sal_Int32 >( maValues.size() ) <= nMaxCount )
261 : {
262 0 : aSettings.maFilterFields.reserve( maValues.size() );
263 :
264 : // insert all filter values
265 0 : aSettings.appendField( true, maValues );
266 :
267 : // extra field for 'show empty'
268 0 : if( mbShowBlank )
269 0 : aSettings.appendField( false, FilterOperator2::EMPTY, OUString() );
270 :
271 : /* Require disabled regular expressions, filter entries may contain
272 : any RE meta characters. */
273 0 : if( !maValues.empty() )
274 0 : aSettings.mobNeedsRegExp = false;
275 : }
276 0 : return aSettings;
277 : }
278 :
279 0 : Top10Filter::Top10Filter( const WorkbookHelper& rHelper ) :
280 : FilterSettingsBase( rHelper ),
281 : mfValue( 0.0 ),
282 : mbTop( true ),
283 0 : mbPercent( false )
284 : {
285 0 : }
286 :
287 0 : void Top10Filter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
288 : {
289 0 : if( nElement == XLS_TOKEN( top10 ) )
290 : {
291 0 : mfValue = rAttribs.getDouble( XML_val, 0.0 );
292 0 : mbTop = rAttribs.getBool( XML_top, true );
293 0 : mbPercent = rAttribs.getBool( XML_percent, false );
294 : }
295 0 : }
296 :
297 0 : void Top10Filter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
298 : {
299 0 : if( nRecId == BIFF12_ID_TOP10FILTER )
300 : {
301 : sal_uInt8 nFlags;
302 0 : nFlags = rStrm.readuChar();
303 0 : mfValue = rStrm.readDouble();
304 0 : mbTop = getFlag( nFlags, BIFF12_TOP10FILTER_TOP );
305 0 : mbPercent = getFlag( nFlags, BIFF12_TOP10FILTER_PERCENT );
306 : }
307 0 : }
308 :
309 0 : ApiFilterSettings Top10Filter::finalizeImport( sal_Int32 /*nMaxCount*/ )
310 : {
311 : sal_Int32 nOperator = mbTop ?
312 : (mbPercent ? FilterOperator2::TOP_PERCENT : FilterOperator2::TOP_VALUES) :
313 0 : (mbPercent ? FilterOperator2::BOTTOM_PERCENT : FilterOperator2::BOTTOM_VALUES);
314 0 : ApiFilterSettings aSettings;
315 0 : aSettings.appendField( true, nOperator, mfValue );
316 0 : return aSettings;
317 : }
318 :
319 1 : FilterCriterionModel::FilterCriterionModel() :
320 : mnOperator( XML_equal ),
321 : mnDataType( BIFF_FILTER_DATATYPE_NONE ),
322 1 : mnStrLen( 0 )
323 : {
324 1 : }
325 :
326 0 : void FilterCriterionModel::setBiffOperator( sal_uInt8 nOperator )
327 : {
328 : static const sal_Int32 spnOperators[] = { XML_TOKEN_INVALID,
329 : XML_lessThan, XML_equal, XML_lessThanOrEqual, XML_greaterThan, XML_notEqual, XML_greaterThanOrEqual };
330 0 : mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
331 0 : }
332 :
333 0 : void FilterCriterionModel::readBiffData( SequenceInputStream& rStrm )
334 : {
335 : sal_uInt8 nOperator;
336 0 : mnDataType = rStrm.readuChar();
337 0 : nOperator = rStrm.readuChar();
338 0 : setBiffOperator( nOperator );
339 :
340 0 : switch( mnDataType )
341 : {
342 : case BIFF_FILTER_DATATYPE_DOUBLE:
343 0 : maValue <<= rStrm.readDouble();
344 0 : break;
345 : case BIFF_FILTER_DATATYPE_STRING:
346 : {
347 0 : rStrm.skip( 8 );
348 0 : OUString aValue = BiffHelper::readString( rStrm ).trim();
349 0 : if( !aValue.isEmpty() )
350 0 : maValue <<= aValue;
351 : }
352 0 : break;
353 : case BIFF_FILTER_DATATYPE_BOOLEAN:
354 0 : maValue <<= (rStrm.readuInt8() != 0);
355 0 : rStrm.skip( 7 );
356 0 : break;
357 : case BIFF_FILTER_DATATYPE_EMPTY:
358 0 : rStrm.skip( 8 );
359 0 : if( mnOperator == XML_equal )
360 0 : maValue <<= OUString();
361 0 : break;
362 : case BIFF_FILTER_DATATYPE_NOTEMPTY:
363 0 : rStrm.skip( 8 );
364 0 : if( mnOperator == XML_notEqual )
365 0 : maValue <<= OUString();
366 0 : break;
367 : default:
368 : OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" );
369 0 : rStrm.skip( 8 );
370 : }
371 0 : }
372 :
373 1 : CustomFilter::CustomFilter( const WorkbookHelper& rHelper ) :
374 : FilterSettingsBase( rHelper ),
375 1 : mbAnd( false )
376 : {
377 1 : }
378 :
379 2 : void CustomFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
380 : {
381 2 : switch( nElement )
382 : {
383 : case XLS_TOKEN( customFilters ):
384 1 : mbAnd = rAttribs.getBool( XML_and, false );
385 1 : break;
386 :
387 : case XLS_TOKEN( customFilter ):
388 : {
389 1 : FilterCriterionModel aCriterion;
390 1 : aCriterion.mnOperator = rAttribs.getToken( XML_operator, XML_equal );
391 2 : OUString aValue = rAttribs.getXString( XML_val, OUString() ).trim();
392 1 : if( (aCriterion.mnOperator == XML_equal) || (aCriterion.mnOperator == XML_notEqual) || (!aValue.isEmpty()) )
393 1 : aCriterion.maValue <<= aValue;
394 2 : appendCriterion( aCriterion );
395 : }
396 1 : break;
397 : }
398 2 : }
399 :
400 0 : void CustomFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
401 : {
402 0 : switch( nRecId )
403 : {
404 : case BIFF12_ID_CUSTOMFILTERS:
405 0 : mbAnd = rStrm.readInt32() == 0;
406 0 : break;
407 :
408 : case BIFF12_ID_CUSTOMFILTER:
409 : {
410 0 : FilterCriterionModel aCriterion;
411 0 : aCriterion.readBiffData( rStrm );
412 0 : appendCriterion( aCriterion );
413 : }
414 0 : break;
415 : }
416 0 : }
417 :
418 1 : ApiFilterSettings CustomFilter::finalizeImport( sal_Int32 /*nMaxCount*/ )
419 : {
420 1 : ApiFilterSettings aSettings;
421 : OSL_ENSURE( maCriteria.size() <= 2, "CustomFilter::finalizeImport - too many filter criteria" );
422 2 : for( FilterCriterionVector::iterator aIt = maCriteria.begin(), aEnd = maCriteria.end(); aIt != aEnd; ++aIt )
423 : {
424 : // first extract the filter operator
425 1 : sal_Int32 nOperator = 0;
426 1 : bool bValidOperator = lclGetApiOperatorFromToken( nOperator, aIt->mnOperator );
427 1 : if( bValidOperator )
428 : {
429 1 : if( aIt->maValue.has< OUString >() )
430 : {
431 : // string argument
432 1 : OUString aValue;
433 1 : aIt->maValue >>= aValue;
434 : // check for 'empty', 'contains', 'begins with', or 'ends with' text filters
435 1 : bool bEqual = nOperator == FilterOperator2::EQUAL;
436 1 : bool bNotEqual = nOperator == FilterOperator2::NOT_EQUAL;
437 1 : if( bEqual || bNotEqual )
438 : {
439 1 : if( aValue.isEmpty() )
440 : {
441 : // empty comparison string: create empty/not empty filters
442 0 : nOperator = bNotEqual ? FilterOperator2::NOT_EMPTY : FilterOperator2::EMPTY;
443 : }
444 : else
445 : {
446 : // compare to something: try to find begins/ends/contains
447 1 : bool bHasLeadingAsterisk = lclTrimLeadingAsterisks( aValue );
448 1 : bool bHasTrailingAsterisk = lclTrimTrailingAsterisks( aValue );
449 : // just '***' matches everything, do not create a filter field
450 1 : bValidOperator = !aValue.isEmpty();
451 1 : if( bValidOperator )
452 : {
453 1 : if( bHasLeadingAsterisk && bHasTrailingAsterisk )
454 0 : nOperator = bNotEqual ? FilterOperator2::DOES_NOT_CONTAIN : FilterOperator2::CONTAINS;
455 1 : else if( bHasLeadingAsterisk )
456 0 : nOperator = bNotEqual ? FilterOperator2::DOES_NOT_END_WITH : FilterOperator2::ENDS_WITH;
457 1 : else if( bHasTrailingAsterisk )
458 0 : nOperator = bNotEqual ? FilterOperator2::DOES_NOT_BEGIN_WITH : FilterOperator2::BEGINS_WITH;
459 : // else: no asterisks, stick to equal/not equal
460 : }
461 : }
462 : }
463 :
464 1 : if( bValidOperator )
465 : {
466 : // if wildcards are present, require RE mode, otherwise keep don't care state
467 1 : if( lclConvertWildcardsToRegExp( aValue ) )
468 0 : aSettings.mobNeedsRegExp = true;
469 : // create a new UNO API filter field
470 1 : aSettings.appendField( mbAnd, nOperator, aValue );
471 1 : }
472 : }
473 0 : else if( aIt->maValue.has< double >() )
474 : {
475 : // floating-point argument
476 0 : double fValue = 0.0;
477 0 : aIt->maValue >>= fValue;
478 0 : aSettings.appendField( mbAnd, nOperator, fValue );
479 : }
480 : }
481 : }
482 1 : return aSettings;
483 : }
484 :
485 1 : void CustomFilter::appendCriterion( const FilterCriterionModel& rCriterion )
486 : {
487 1 : if( (rCriterion.mnOperator != XML_TOKEN_INVALID) && rCriterion.maValue.hasValue() )
488 1 : maCriteria.push_back( rCriterion );
489 1 : }
490 :
491 1 : FilterColumn::FilterColumn( const WorkbookHelper& rHelper ) :
492 : WorkbookHelper( rHelper ),
493 : mnColId( -1 ),
494 : mbHiddenButton( false ),
495 1 : mbShowButton( true )
496 : {
497 1 : }
498 :
499 1 : void FilterColumn::importFilterColumn( const AttributeList& rAttribs )
500 : {
501 1 : mnColId = rAttribs.getInteger( XML_colId, -1 );
502 1 : mbHiddenButton = rAttribs.getBool( XML_hiddenButton, false );
503 1 : mbShowButton = rAttribs.getBool( XML_showButton, true );
504 1 : }
505 :
506 0 : void FilterColumn::importFilterColumn( SequenceInputStream& rStrm )
507 : {
508 : sal_uInt16 nFlags;
509 0 : mnColId = rStrm.readInt32();
510 0 : nFlags = rStrm.readuInt16();
511 0 : mbHiddenButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_HIDDENBUTTON );
512 0 : mbShowButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_SHOWBUTTON );
513 0 : }
514 :
515 1 : ApiFilterSettings FilterColumn::finalizeImport( sal_Int32 nMaxCount )
516 : {
517 1 : ApiFilterSettings aSettings;
518 1 : if( (0 <= mnColId) && mxSettings.get() )
519 : {
520 : // filter settings object creates a sequence of filter fields
521 1 : aSettings = mxSettings->finalizeImport( nMaxCount );
522 : // add column index to all filter fields
523 2 : for( ApiFilterSettings::FilterFieldVector::iterator aIt = aSettings.maFilterFields.begin(), aEnd = aSettings.maFilterFields.end(); aIt != aEnd; ++aIt )
524 1 : aIt->Field = mnColId;
525 : }
526 1 : return aSettings;
527 : }
528 :
529 1 : AutoFilter::AutoFilter( const WorkbookHelper& rHelper ) :
530 1 : WorkbookHelper( rHelper )
531 : {
532 1 : }
533 :
534 1 : void AutoFilter::importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet )
535 : {
536 1 : OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
537 1 : AddressConverter::convertToCellRangeUnchecked( maRange, aRangeStr, nSheet );
538 1 : }
539 :
540 0 : void AutoFilter::importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet )
541 : {
542 0 : BinRange aBinRange;
543 0 : rStrm >> aBinRange;
544 0 : AddressConverter::convertToCellRangeUnchecked( maRange, aBinRange, nSheet );
545 0 : }
546 :
547 1 : FilterColumn& AutoFilter::createFilterColumn()
548 : {
549 1 : FilterColumnVector::value_type xFilterColumn( new FilterColumn( *this ) );
550 1 : maFilterColumns.push_back( xFilterColumn );
551 1 : return *xFilterColumn;
552 : }
553 :
554 1 : void AutoFilter::finalizeImport( const Reference<XSheetFilterDescriptor3>& rxFilterDesc )
555 : {
556 1 : if( rxFilterDesc.is() )
557 : {
558 : // set some common properties for the auto filter range
559 1 : PropertySet aDescProps( rxFilterDesc );
560 1 : aDescProps.setProperty( PROP_IsCaseSensitive, false );
561 1 : aDescProps.setProperty( PROP_SkipDuplicates, false );
562 1 : aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
563 1 : aDescProps.setProperty( PROP_ContainsHeader, true );
564 1 : aDescProps.setProperty( PROP_CopyOutputData, false );
565 :
566 : // maximum number of UNO API filter fields
567 1 : sal_Int32 nMaxCount = 0;
568 1 : aDescProps.getProperty( nMaxCount, PROP_MaxFieldCount );
569 : OSL_ENSURE( nMaxCount > 0, "AutoFilter::finalizeImport - invalid maximum filter field count" );
570 :
571 : // resulting list of all UNO API filter fields
572 2 : ::std::vector<TableFilterField3> aFilterFields;
573 :
574 : // track if columns require to enable or disable regular expressions
575 1 : OptValue< bool > obNeedsRegExp;
576 :
577 : /* Track whether the filter fields of the first filter column are
578 : connected with 'or'. In this case, other filter fields cannot be
579 : inserted without altering the result of the entire filter, due to
580 : Calc's precedence for the 'and' connection operator. Example:
581 : Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and
582 : B2 belong to filter column B, will be evaluated by Calc as
583 : '(A1 and B1) or (B2 and C1)'. */
584 1 : bool bHasOrConnection = false;
585 :
586 : // process all filter column objects, exit when 'or' connection exists
587 2 : for( FilterColumnVector::iterator aIt = maFilterColumns.begin(), aEnd = maFilterColumns.end(); !bHasOrConnection && (aIt != aEnd); ++aIt )
588 : {
589 : // the filter settings object creates a list of filter fields
590 1 : ApiFilterSettings aSettings = (*aIt)->finalizeImport( nMaxCount );
591 1 : ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields;
592 :
593 : // new total number of filter fields
594 1 : sal_Int32 nNewCount = static_cast< sal_Int32 >( aFilterFields.size() + rColumnFields.size() );
595 :
596 : /* Check whether mode for regular expressions is compatible with
597 : the global mode in obNeedsRegExp. If either one is still in
598 : don't-care state, all is fine. If both are set, they must be
599 : equal. */
600 1 : bool bRegExpCompatible = !obNeedsRegExp || !aSettings.mobNeedsRegExp || (obNeedsRegExp.get() == aSettings.mobNeedsRegExp.get());
601 :
602 : // check whether fields are connected by 'or' (see comments above).
603 1 : if( rColumnFields.size() >= 2 )
604 0 : for( ApiFilterSettings::FilterFieldVector::iterator aSIt = rColumnFields.begin() + 1, aSEnd = rColumnFields.end(); !bHasOrConnection && (aSIt != aSEnd); ++aSIt )
605 0 : bHasOrConnection = aSIt->Connection == FilterConnection_OR;
606 :
607 : /* Skip the column filter, if no filter fields have been created,
608 : if the number of new filter fields would exceed the total limit
609 : of filter fields, or if the mode for regular expressions of the
610 : filter column does not fit. */
611 1 : if( !rColumnFields.empty() && (nNewCount <= nMaxCount) && bRegExpCompatible )
612 : {
613 : /* Add 'and' connection to the first filter field to connect
614 : it to the existing filter fields of other columns. */
615 1 : rColumnFields[ 0 ].Connection = FilterConnection_AND;
616 :
617 : // insert the new filter fields
618 1 : aFilterFields.insert( aFilterFields.end(), rColumnFields.begin(), rColumnFields.end() );
619 :
620 : // update the regular expressions mode
621 1 : obNeedsRegExp.assignIfUsed( aSettings.mobNeedsRegExp );
622 : }
623 1 : }
624 :
625 : // insert all filter fields to the filter descriptor
626 1 : if( !aFilterFields.empty() )
627 1 : rxFilterDesc->setFilterFields3( ContainerHelper::vectorToSequence( aFilterFields ) );
628 :
629 : // regular expressions
630 1 : bool bUseRegExp = obNeedsRegExp.get( false );
631 2 : aDescProps.setProperty( PROP_UseRegularExpressions, bUseRegExp );
632 : }
633 1 : }
634 :
635 269 : AutoFilterBuffer::AutoFilterBuffer( const WorkbookHelper& rHelper ) :
636 269 : WorkbookHelper( rHelper )
637 : {
638 269 : }
639 :
640 1 : AutoFilter& AutoFilterBuffer::createAutoFilter()
641 : {
642 1 : AutoFilterVector::value_type xAutoFilter( new AutoFilter( *this ) );
643 1 : maAutoFilters.push_back( xAutoFilter );
644 1 : return *xAutoFilter;
645 : }
646 :
647 269 : void AutoFilterBuffer::finalizeImport( sal_Int16 nSheet )
648 : {
649 : // rely on existence of the defined name '_FilterDatabase' containing the range address of the filtered area
650 269 : if( const DefinedName* pFilterDBName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_FILTERDATABASE, nSheet ).get() )
651 : {
652 1 : CellRangeAddress aFilterRange;
653 1 : if( pFilterDBName->getAbsoluteRange( aFilterRange ) && (aFilterRange.Sheet == nSheet) )
654 : {
655 : // use the same name for the database range as used for the defined name '_FilterDatabase'
656 1 : Reference< XDatabaseRange > xDatabaseRange = createUnnamedDatabaseRangeObject( aFilterRange );
657 : // first, try to create an auto filter
658 1 : bool bHasAutoFilter = finalizeImport( xDatabaseRange );
659 : // no success: try to create an advanced filter
660 1 : if( !bHasAutoFilter && xDatabaseRange.is() )
661 : {
662 : // the built-in defined name 'Criteria' must exist
663 0 : if( const DefinedName* pCriteriaName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_CRITERIA, nSheet ).get() )
664 : {
665 0 : CellRangeAddress aCriteriaRange;
666 0 : if( pCriteriaName->getAbsoluteRange( aCriteriaRange ) )
667 : {
668 : // set some common properties for the filter descriptor
669 0 : PropertySet aDescProps( xDatabaseRange->getFilterDescriptor() );
670 0 : aDescProps.setProperty( PROP_IsCaseSensitive, false );
671 0 : aDescProps.setProperty( PROP_SkipDuplicates, false );
672 0 : aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
673 0 : aDescProps.setProperty( PROP_ContainsHeader, true );
674 : // criteria range may contain wildcards, but these are incompatible with REs
675 0 : aDescProps.setProperty( PROP_UseRegularExpressions, false );
676 :
677 : // position of output data (if built-in defined name 'Extract' exists)
678 0 : DefinedNameRef xExtractName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_EXTRACT, nSheet );
679 0 : CellRangeAddress aOutputRange;
680 0 : bool bHasOutputRange = xExtractName.get() && xExtractName->getAbsoluteRange( aOutputRange );
681 0 : aDescProps.setProperty( PROP_CopyOutputData, bHasOutputRange );
682 0 : if( bHasOutputRange )
683 : {
684 0 : aDescProps.setProperty( PROP_SaveOutputPosition, true );
685 0 : aDescProps.setProperty( PROP_OutputPosition, CellAddress( aOutputRange.Sheet, aOutputRange.StartColumn, aOutputRange.StartRow ) );
686 : }
687 :
688 : /* Properties of the database range (must be set after
689 : modifying properties of the filter descriptor,
690 : otherwise the 'FilterCriteriaSource' property gets
691 : deleted). */
692 0 : PropertySet aRangeProps( xDatabaseRange );
693 0 : aRangeProps.setProperty( PROP_AutoFilter, false );
694 0 : aRangeProps.setProperty( PROP_FilterCriteriaSource, aCriteriaRange );
695 : }
696 : }
697 1 : }
698 : }
699 : }
700 269 : }
701 :
702 1 : bool AutoFilterBuffer::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRange )
703 : {
704 1 : AutoFilter* pAutoFilter = getActiveAutoFilter();
705 1 : if( pAutoFilter && rxDatabaseRange.is() ) try
706 : {
707 : // the property 'AutoFilter' enables the drop-down buttons
708 1 : PropertySet aRangeProps( rxDatabaseRange );
709 1 : aRangeProps.setProperty( PROP_AutoFilter, true );
710 : // convert filter settings using the filter descriptor of the database range
711 2 : Reference< XSheetFilterDescriptor3 > xFilterDesc( rxDatabaseRange->getFilterDescriptor(), UNO_QUERY_THROW );
712 1 : pAutoFilter->finalizeImport( xFilterDesc );
713 : // return true to indicate enabled autofilter
714 2 : return true;
715 : }
716 0 : catch( Exception& )
717 : {
718 : }
719 0 : return false;
720 : }
721 :
722 1 : AutoFilter* AutoFilterBuffer::getActiveAutoFilter()
723 : {
724 : // Excel expects not more than one auto filter per sheet or table
725 : OSL_ENSURE( maAutoFilters.size() <= 1, "AutoFilterBuffer::getActiveAutoFilter - too many auto filters" );
726 : // stick to the last imported auto filter
727 1 : return maAutoFilters.empty() ? 0 : maAutoFilters.back().get();
728 : }
729 :
730 : } // namespace xls
731 30 : } // namespace oox
732 :
733 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|