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