summaryrefslogtreecommitdiff
path: root/sc/inc/queryiter.hxx
blob: 67f0e16fc0b5237f4b4039d9223c62503dd0acbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#pragma once

#include "queryparam.hxx"
#include "mtvelements.hxx"
#include "types.hxx"

struct ScComplexRefData;
class ScSortedRangeCache;

/*
Query-related iterators. There is one template class ScQueryCellIteratorBase
that implements most of the shared functionality, specific parts are done
by specializing the templates and then subclassing as the actual class to use.
A template is used for maximum performance, as that allows fast code specializing,
inlining, etc.
There are two template arguments:
* ScQueryCellIteratorAccess specifies how cells are accessed:
  + Direct - direct access to cells using mdds.
  + SortedCache - for accessing unsorted cells in a sorted way using ScSortedRangeCache.
* ScQueryCellIteratorType specifies the type of the query operation:
  + Generic - the generic lookup, used e.g. by VLOOKUP.
  + CountIf - faster implementation for COUNTIF(S).

Specific data should be in specific templated base classes, otherwise adding data
members would mean specializing the entire ScQueryCellIteratorBase. Some specific
functionality may also be implemented in the base classes or depending on the template
parameter.
*/

// Data and functionality for accessing cells in a specific way.
// Needs specialization, see ScQueryCellIteratorAccess::Direct for what is needed.
template< ScQueryCellIteratorAccess accessType >
class ScQueryCellIteratorAccessSpecific
{
};

// The implementation using linear direct mdds access.
template<>
class ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >
{
protected:
    ScQueryCellIteratorAccessSpecific( ScDocument& rDocument, ScInterpreterContext& rContext,
        const ScQueryParam& rParam, bool bReverseSearch );
    // Initialize position for new column.
    void InitPos();
    // Increase position (next row).
    void IncPos();
    // Next mdds block. If access is not direct/linear, then
    // should call IncPos().
    void IncBlock();

    // Decrease position (prev row).
    void DecPos();
    // Prev mdds block. If access is not direct/linear, then
    // should call DecPos().
    void DecBlock();

    // These members needs to be available already in the base class.
    typedef sc::CellStoreType::const_position_type PositionType;
    PositionType maCurPos;
    ScQueryParam    maParam;
    ScDocument&     rDoc;
    ScInterpreterContext& mrContext;
    bool            mbReverseSearch;
    SCTAB           nTab;
    SCCOL           nCol;
    SCROW           nRow;

    class NonEmptyCellIndexer;
    typedef std::pair<ScRefCellValue, SCCOLROW> BinarySearchCellType;
    static NonEmptyCellIndexer MakeBinarySearchIndexer(const sc::CellStoreType* pCells,
        SCCOLROW nStartRow, SCCOLROW nEndRow);
};

// The implementation using ScSortedRangeCache, which allows sorted iteration
// of unsorted cells.
template<>
class ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >
{
public:
    void SetSortedRangeCache( const ScSortedRangeCache& cache );
    template<bool fast>
    bool IncPosImpl();
protected:
    ScQueryCellIteratorAccessSpecific( ScDocument& rDocument, ScInterpreterContext& rContext,
        const ScQueryParam& rParam, bool bReverseSearch );
    void InitPosStart(bool bNewSearchFunction, sal_uInt8 nSortedBinarySearch = 0x00);
    void InitPosFinish( SCROW beforeRow, SCROW lastRow, bool bFirstMatch );
    void InitPosColFinish( SCCOL beforeCol, SCCOL lastCol, bool bFirstMatch );
    void IncPos() { IncPosImpl<false>(); }
    bool IncPosFast() { return IncPosImpl<true>(); }
    void IncBlock() { IncPos(); } // Cannot skip entire block, not linear.

    // Initialize for backward search. (no need for SortedCache)
    static void DecPos() {};
    static void DecBlock() {};

    // These members needs to be available already in the base class.
    typedef sc::CellStoreType::const_position_type PositionType;
    PositionType maCurPos;
    ScQueryParam    maParam;
    ScDocument&     rDoc;
    ScInterpreterContext& mrContext;
    bool            mbReverseSearch;
    SCTAB           nTab;
    SCCOL           nCol;
    SCROW           nRow;
    const ScColumn* pColumn; // matching nCol, set by InitPos()

    const ScSortedRangeCache* sortedCache;
    size_t sortedCachePos;
    size_t sortedCachePosLast;

    class SortedCacheIndexer;
    typedef std::pair<ScRefCellValue, SCCOLROW> BinarySearchCellType;
    SortedCacheIndexer MakeBinarySearchIndexer(const sc::CellStoreType* pCells,
        SCCOLROW nStartRow, SCCOLROW nEndRow);
};

// Data and functionality for specific types of query.
template< ScQueryCellIteratorType iteratorType >
class ScQueryCellIteratorTypeSpecific
{
protected:
    bool HandleItemFound(); // not implemented, needs specialization
};

// Shared code for query-based iterators. The main class.
template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
class ScQueryCellIteratorBase
    : public ScQueryCellIteratorAccessSpecific< accessType >
    , public ScQueryCellIteratorTypeSpecific< queryType >
{
    typedef ScQueryCellIteratorAccessSpecific< accessType > AccessBase;
    typedef ScQueryCellIteratorTypeSpecific< queryType > TypeBase;
protected:
    enum StopOnMismatchBits
    {
        nStopOnMismatchDisabled = 0x00,
        nStopOnMismatchEnabled  = 0x01,
        nStopOnMismatchOccurred  = 0x02,
        nStopOnMismatchExecuted = nStopOnMismatchEnabled | nStopOnMismatchOccurred
    };

    enum TestEqualConditionBits
    {
        nTestEqualConditionDisabled = 0x00,
        nTestEqualConditionEnabled  = 0x01,
        nTestEqualConditionMatched  = 0x02,
        nTestEqualConditionFulfilled = nTestEqualConditionEnabled | nTestEqualConditionMatched
    };

    enum SortedBinarySearchBits
    {
        nBinarySearchDisabled = 0x00,
        nSearchbAscd = 0x01,
        nSearchbDesc = 0x02,
    };

    sal_uInt8            nStopOnMismatch;
    sal_uInt8            nTestEqualCondition;
    sal_uInt8            nSortedBinarySearch;
    bool            bAdvanceQuery;
    bool            bIgnoreMismatchOnLeadingStrings;
    sal_uInt16      nSearchOpCode;
    SCCOL           nBestFitCol;
    SCROW           nBestFitRow;

    // Make base members directly visible here (templated bases need 'this->').
    using AccessBase::maCurPos;
    using AccessBase::maParam;
    using AccessBase::rDoc;
    using AccessBase::mrContext;
    using AccessBase::mbReverseSearch;
    using AccessBase::nTab;
    using AccessBase::nCol;
    using AccessBase::nRow;
    using AccessBase::IncPos;
    using AccessBase::IncBlock;
    using AccessBase::DecPos;
    using AccessBase::DecBlock;
    using typename AccessBase::BinarySearchCellType;
    using AccessBase::MakeBinarySearchIndexer;
    using TypeBase::HandleItemFound;

    void InitPos();

    // The actual query function. It will call HandleItemFound() for any matching type
    // and return if HandleItemFound() returns true.
    void PerformQuery();

    /* Only works if no regular expression is involved, only searches for rows in one column or
       only searches for cols in one row, and only the first query entry is considered with simple
       conditions SC_LESS,SC_LESS_EQUAL, SC_EQUAL (sorted ascending) or SC_GREATER,SC_GREATER_EQUAL
       (sorted descending). It delivers a starting point set to nRow/nCol, i.e. the last row/col that
       either matches the searched for value, or the last row/col that matches the condition.
       Continue with e.g. GetThis() and GetNext() afterwards. Returns false if the searched for value
       is not in the search range or if the range is not properly sorted, with nRow/nCol in that case
       set to the first row or after the last row. In that case use GetFirst().
    */
    bool BinarySearch( SCCOLROW col_row, bool forEqual = false );

                    /** If set, iterator stops on first non-matching cell
                        content. May be used in SC_LESS_EQUAL queries where a
                        cell range is assumed to be sorted; stops on first
                        value being greater than the queried value and
                        GetFirst()/GetNext() return NULL. StoppedOnMismatch()
                        returns true then.
                        However, the iterator's conditions are not set to end
                        all queries, GetCol() and GetRow() return values for
                        the non-matching cell, further GetNext() calls may be
                        executed. */
    void            SetStopOnMismatch( bool bVal )
                        {
                            nStopOnMismatch = sal::static_int_cast<sal_uInt8>(bVal ? nStopOnMismatchEnabled :
                                nStopOnMismatchDisabled);
                        }
    bool            StoppedOnMismatch() const
                        { return nStopOnMismatch == nStopOnMismatchExecuted; }

                    /** If set, an additional test for SC_EQUAL condition is
                        executed in ScTable::ValidQuery() if SC_LESS_EQUAL or
                        SC_GREATER_EQUAL conditions are to be tested. May be
                        used where a cell range is assumed to be sorted to stop
                        if an equal match is found. */
    void            SetTestEqualCondition( bool bVal )
                        {
                            nTestEqualCondition = sal::static_int_cast<sal_uInt8>(bVal ?
                                nTestEqualConditionEnabled :
                                nTestEqualConditionDisabled);
                        }
    bool            IsEqualConditionFulfilled() const
                        { return nTestEqualCondition == nTestEqualConditionFulfilled; }

    void            HandleBestFitItemFound(SCCOL nBFitCol, SCROW nBFitRow)
                        {
                            nBestFitCol = nBFitCol;
                            nBestFitRow = nBFitRow;
                        }
public:
                    ScQueryCellIteratorBase(ScDocument& rDocument, ScInterpreterContext& rContext, SCTAB nTable,
                                            const ScQueryParam& aParam, bool bMod, bool bReverse);
                                        // when !bMod, the QueryParam has to be filled
                                        // (bIsString)

                    // increments all Entry.nField, if column
                    // changes, for ScInterpreter ScHLookup()
    void            SetAdvanceQueryParamEntryField( bool bVal )
                        { bAdvanceQuery = bVal; }
    void            AdvanceQueryParamEntryField();
    void            AdvanceQueryParamEntryFieldForBinarySearch();

    void            SetSortedBinarySearchMode( sal_Int8 nSearchMode )
                        {
                            nSortedBinarySearch = sal::static_int_cast<sal_uInt8>(nSearchMode == 2 ?
                                nSearchbAscd : (nSearchMode == -2 ? nSearchbDesc : nBinarySearchDisabled));
                        }

    void            SetLookupMode( sal_uInt16 nVal )
                        { nSearchOpCode = nVal; }
};


template<>
class ScQueryCellIteratorTypeSpecific< ScQueryCellIteratorType::Generic >
{
protected:
    bool HandleItemFound();
    bool getThisResult;
};

// The generic query iterator, used e.g. by VLOOKUP.
template< ScQueryCellIteratorAccess accessType >
class ScQueryCellIterator
    : public ScQueryCellIteratorBase< accessType, ScQueryCellIteratorType::Generic >
{
    typedef ScQueryCellIteratorBase< accessType, ScQueryCellIteratorType::Generic > Base;
    // Make base members directly visible here (templated bases need 'this->').
    using Base::maParam;
    using Base::rDoc;
    using Base::mrContext;
    using Base::mbReverseSearch;
    using Base::nTab;
    using Base::nCol;
    using Base::nRow;
    using Base::InitPos;
    using Base::IncPos;
    using Base::DecPos;
    using Base::bIgnoreMismatchOnLeadingStrings;
    using Base::SetStopOnMismatch;
    using Base::SetTestEqualCondition;
    using Base::BinarySearch;
    using typename Base::PositionType;
    using Base::maCurPos;
    using Base::IsEqualConditionFulfilled;
    using Base::bAdvanceQuery;
    using Base::StoppedOnMismatch;
    using Base::nStopOnMismatch;
    using Base::nStopOnMismatchEnabled;
    using Base::nTestEqualCondition;
    using Base::nTestEqualConditionEnabled;
    using Base::nSortedBinarySearch;
    using Base::nBinarySearchDisabled;
    using Base::PerformQuery;
    using Base::getThisResult;
    using Base::nBestFitCol;
    using Base::nBestFitRow;
    using Base::nSearchOpCode;

    bool GetThis();

public:
    ScQueryCellIterator(ScDocument& rDocument, ScInterpreterContext& rContext, SCTAB nTable,
                        const ScQueryParam& aParam, bool bMod, bool bReverse)
        : Base( rDocument, rContext, nTable, aParam, bMod, bReverse ) {}
    bool GetFirst();
    bool GetNext();
    SCCOL GetCol() const { return nCol; }
    SCROW GetRow() const { return nRow; }

                    /** In a range assumed to be sorted find either the last of
                        a sequence of equal entries or the last being less than
                        (or greater than) the queried value. Used by the
                        interpreter for [HV]?LOOKUP() and MATCH(). Column and
                        row position of the found entry are returned, otherwise
                        invalid.

                        The search does not stop when encountering a string and does not
                        assume that no values follow anymore.
                        If querying for a string a mismatch on the first
                        entry, e.g. column header, is ignored.

                        @ATTENTION! StopOnMismatch, TestEqualCondition and
                        the internal IgnoreMismatchOnLeadingStrings and query
                        params are in an undefined state upon return! The
                        iterator is not usable anymore except for obtaining the
                        number format!
                      */
    bool            FindEqualOrSortedLastInRange( SCCOL& nFoundCol, SCROW& nFoundRow );
};

typedef ScQueryCellIterator< ScQueryCellIteratorAccess::Direct > ScQueryCellIteratorDirect;

class ScQueryCellIteratorSortedCache
    : public ScQueryCellIterator< ScQueryCellIteratorAccess::SortedCache >
{
    typedef ScQueryCellIterator< ScQueryCellIteratorAccess::SortedCache > Base;
public:
    ScQueryCellIteratorSortedCache(ScDocument& rDocument, ScInterpreterContext& rContext,
        SCTAB nTable, const ScQueryParam& aParam, bool bMod, bool bReverse )
    : Base( rDocument, rContext, nTable, aParam, bMod, bReverse ) {}
    // Returns true if this iterator can be used for the given query.
    static bool CanBeUsed(ScDocument& rDoc, const ScQueryParam& aParam,
        SCTAB nTab, const ScFormulaCell* cell, const ScComplexRefData* refData,
        ScInterpreterContext& context);
};


template<>
class ScQueryCellIteratorTypeSpecific< ScQueryCellIteratorType::CountIf >
{
protected:
    bool HandleItemFound();
    sal_uInt64 countIfCount;
};

// Used by ScInterpreter::ScCountIf.
template< ScQueryCellIteratorAccess accessType >
class ScCountIfCellIterator
    : public ScQueryCellIteratorBase< accessType, ScQueryCellIteratorType::CountIf >
{
protected:
    typedef ScQueryCellIteratorBase< accessType, ScQueryCellIteratorType::CountIf > Base;
    // Make base members directly visible here (templated bases need 'this->').
    using Base::maParam;
    using Base::rDoc;
    using Base::nTab;
    using Base::nCol;
    using Base::nRow;
    using Base::InitPos;
    using Base::PerformQuery;
    using Base::SetAdvanceQueryParamEntryField;
    using Base::countIfCount;

public:
    ScCountIfCellIterator(ScDocument& rDocument, ScInterpreterContext& rContext, SCTAB nTable,
                          const ScQueryParam& aParam, bool bMod, bool bReverse)
        : Base( rDocument, rContext, nTable, aParam, bMod, bReverse ) {}
    sal_uInt64 GetCount();
};

typedef ScCountIfCellIterator< ScQueryCellIteratorAccess::Direct > ScCountIfCellIteratorDirect;

class ScCountIfCellIteratorSortedCache
    : public ScCountIfCellIterator< ScQueryCellIteratorAccess::SortedCache >
{
    typedef ScCountIfCellIterator< ScQueryCellIteratorAccess::SortedCache > Base;
public:
    ScCountIfCellIteratorSortedCache(ScDocument& rDocument, ScInterpreterContext& rContext,
        SCTAB nTable, const ScQueryParam& aParam, bool bMod, bool bReverse)
    : Base( rDocument, rContext, nTable, aParam, bMod, bReverse ) {}
    // Returns true if this iterator can be used for the given query.
    static bool CanBeUsed(ScDocument& rDoc, const ScQueryParam& aParam,
        SCTAB nTab, const ScFormulaCell* cell, const ScComplexRefData* refData,
        ScInterpreterContext& context);
};

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */