1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <doubleref.hxx>
22 #include <document.hxx>
23 #include <queryparam.hxx>
24 #include <queryentry.hxx>
25 #include <globstr.hrc>
26 #include <scresid.hxx>
27 #include <scmatrix.hxx>
29 #include <svl/sharedstringpool.hxx>
30 #include <osl/diagnose.h>
31 #include <unotools/charclass.hxx>
32 #include <unotools/transliterationwrapper.hxx>
38 using ::std::unique_ptr
;
43 void lcl_uppercase(OUString
& rStr
)
45 rStr
= ScGlobal::getCharClass().uppercase(rStr
.trim());
48 bool lcl_createStarQuery(
49 const ScDocument
* pDoc
,
50 svl::SharedStringPool
& rPool
, ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
, const ScDBRangeBase
* pQueryRef
)
52 // A valid StarQuery must be at least 4 columns wide. To be precise it
53 // should be exactly 4 columns ...
54 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
55 // column Excel style query range immediately left to itself would result
56 // in a circular reference when the field name or operator or value (first
57 // to third query range column) is obtained (#i58354#). Furthermore, if the
58 // range wasn't sufficiently specified data changes wouldn't flag formula
59 // cells for recalculation.
61 if (pQueryRef
->getColSize() < 4)
68 SCROW nRows
= pDBRef
->getRowSize();
69 SCSIZE nNewEntries
= static_cast<SCSIZE
>(nRows
);
70 pParam
->Resize(nNewEntries
);
74 ScQueryEntry
& rEntry
= pParam
->GetEntry(nIndex
);
80 // For all entries after the first one, check the and/or connector in the first column.
81 aCellStr
= pQueryRef
->getString(0, nRow
);
82 lcl_uppercase(aCellStr
);
83 if ( aCellStr
== ScResId(STR_TABLE_AND
) )
85 rEntry
.eConnect
= SC_AND
;
88 else if ( aCellStr
== ScResId(STR_TABLE_OR
) )
90 rEntry
.eConnect
= SC_OR
;
95 if ((nIndex
< 1) || bValid
)
97 // field name in the 2nd column.
98 aCellStr
= pQueryRef
->getString(1, nRow
);
99 SCCOL nField
= pDBRef
->findFieldColumn(aCellStr
); // TODO: must be case insensitive comparison.
100 if (pDoc
->ValidCol(nField
))
102 rEntry
.nField
= nField
;
111 // equality, non-equality operator in the 3rd column.
112 aCellStr
= pQueryRef
->getString(2, nRow
);
113 lcl_uppercase(aCellStr
);
114 const sal_Unicode
* p
= aCellStr
.getStr();
118 rEntry
.eOp
= SC_NOT_EQUAL
;
119 else if (p
[1] == '=')
120 rEntry
.eOp
= SC_LESS_EQUAL
;
122 rEntry
.eOp
= SC_LESS
;
124 else if (p
[0] == '>')
127 rEntry
.eOp
= SC_GREATER_EQUAL
;
129 rEntry
.eOp
= SC_GREATER
;
131 else if (p
[0] == '=')
132 rEntry
.eOp
= SC_EQUAL
;
138 // Finally, the right-hand-side value in the 4th column.
139 rEntry
.GetQueryItem().maString
=
140 rPool
.intern(pQueryRef
->getString(3, nRow
));
141 rEntry
.bDoQuery
= true;
146 while (bValid
&& (nRow
< nRows
) /* && (nIndex < MAXQUERY) */ );
150 bool lcl_createExcelQuery(
151 const ScDocument
* pDoc
,
152 svl::SharedStringPool
& rPool
, ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
, const ScDBRangeBase
* pQueryRef
)
155 SCCOL nCols
= pQueryRef
->getColSize();
156 SCROW nRows
= pQueryRef
->getRowSize();
157 vector
<SCCOL
> aFields(nCols
);
159 while (bValid
&& (nCol
< nCols
))
161 OUString aQueryStr
= pQueryRef
->getString(nCol
, 0);
162 SCCOL nField
= pDBRef
->findFieldColumn(aQueryStr
);
163 if (pDoc
->ValidCol(nField
))
164 aFields
[nCol
] = nField
;
172 // Count the number of visible cells (excluding the header row). Each
173 // visible cell corresponds with a single query.
174 SCSIZE nVisible
= pQueryRef
->getVisibleDataCellCount();
175 if ( nVisible
> SCSIZE_MAX
/ sizeof(void*) )
177 OSL_FAIL("too many filter criteria");
181 SCSIZE nNewEntries
= nVisible
;
182 pParam
->Resize( nNewEntries
);
192 aCellStr
= pQueryRef
->getString(nCol
, nRow
);
193 aCellStr
= ScGlobal::getCharClass().uppercase( aCellStr
);
194 if (!aCellStr
.isEmpty())
196 if (nIndex
< nNewEntries
)
198 pParam
->GetEntry(nIndex
).nField
= aFields
[nCol
];
199 pParam
->FillInExcelSyntax(rPool
, aCellStr
, nIndex
, nullptr);
201 if (nIndex
< nNewEntries
)
202 pParam
->GetEntry(nIndex
).eConnect
= SC_AND
;
210 if (nIndex
< nNewEntries
)
211 pParam
->GetEntry(nIndex
).eConnect
= SC_OR
;
217 bool lcl_fillQueryEntries(
218 const ScDocument
* pDoc
,
219 svl::SharedStringPool
& rPool
, ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
, const ScDBRangeBase
* pQueryRef
)
221 SCSIZE nCount
= pParam
->GetEntryCount();
222 for (SCSIZE i
= 0; i
< nCount
; ++i
)
223 pParam
->GetEntry(i
).Clear();
225 // Standard QueryTabelle
226 bool bValid
= lcl_createStarQuery(pDoc
, rPool
, pParam
, pDBRef
, pQueryRef
);
227 // Excel QueryTabelle
229 bValid
= lcl_createExcelQuery(pDoc
, rPool
, pParam
, pDBRef
, pQueryRef
);
231 nCount
= pParam
->GetEntryCount();
234 // bQueryByString must be set
235 for (SCSIZE i
= 0; i
< nCount
; ++i
)
236 pParam
->GetEntry(i
).GetQueryItem().meType
= ScQueryEntry::ByString
;
241 for (SCSIZE i
= 0; i
< nCount
; ++i
)
242 pParam
->GetEntry(i
).Clear();
249 ScDBRangeBase::ScDBRangeBase(ScDocument
* pDoc
) :
254 ScDBRangeBase::~ScDBRangeBase()
258 bool ScDBRangeBase::fillQueryEntries(ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
) const
263 return lcl_fillQueryEntries(getDoc(), getDoc()->GetSharedStringPool(), pParam
, pDBRef
, this);
266 void ScDBRangeBase::fillQueryOptions(ScQueryParamBase
* pParam
)
268 pParam
->bHasHeader
= true;
269 pParam
->bByRow
= true;
270 pParam
->bInplace
= true;
271 pParam
->bCaseSens
= false;
272 pParam
->eSearchType
= utl::SearchParam::SearchType::Normal
;
273 pParam
->bDuplicate
= true;
276 ScDBInternalRange::ScDBInternalRange(ScDocument
* pDoc
, const ScRange
& rRange
) :
277 ScDBRangeBase(pDoc
), maRange(rRange
)
281 ScDBInternalRange::~ScDBInternalRange()
285 SCCOL
ScDBInternalRange::getColSize() const
287 return maRange
.aEnd
.Col() - maRange
.aStart
.Col() + 1;
290 SCROW
ScDBInternalRange::getRowSize() const
292 return maRange
.aEnd
.Row() - maRange
.aStart
.Row() + 1;
295 SCSIZE
ScDBInternalRange::getVisibleDataCellCount() const
297 SCCOL nCols
= getColSize();
298 SCROW nRows
= getRowSize();
302 return (nRows
-1)*nCols
;
305 OUString
ScDBInternalRange::getString(SCCOL nCol
, SCROW nRow
) const
307 const ScAddress
& s
= maRange
.aStart
;
308 // #i109200# this is used in formula calculation, use GetInputString, not GetString
309 // (consistent with ScDBInternalRange::getCellString)
310 // GetStringForFormula is not used here, to allow querying for date values.
311 return getDoc()->GetInputString(s
.Col() + nCol
, s
.Row() + nRow
, maRange
.aStart
.Tab());
314 SCCOL
ScDBInternalRange::getFirstFieldColumn() const
316 return getRange().aStart
.Col();
319 SCCOL
ScDBInternalRange::findFieldColumn(SCCOL nIndex
) const
321 const ScRange
& rRange
= getRange();
322 const ScAddress
& s
= rRange
.aStart
;
324 SCCOL nDBCol1
= s
.Col();
326 // Don't handle out-of-bound condition here. We'll do that later.
327 return nIndex
+ nDBCol1
- 1;
330 SCCOL
ScDBInternalRange::findFieldColumn(const OUString
& rStr
, FormulaError
* pErr
) const
332 const ScAddress
& s
= maRange
.aStart
;
333 const ScAddress
& e
= maRange
.aEnd
;
334 OUString aUpper
= rStr
;
335 lcl_uppercase(aUpper
);
337 SCCOL nDBCol1
= s
.Col();
338 SCROW nDBRow1
= s
.Row();
339 SCTAB nDBTab1
= s
.Tab();
340 SCCOL nDBCol2
= e
.Col();
345 ScAddress
aLook( nDBCol1
, nDBRow1
, nDBTab1
);
346 while (!bFound
&& (aLook
.Col() <= nDBCol2
))
348 FormulaError nErr
= getDoc()->GetStringForFormula( aLook
, aCellStr
);
351 lcl_uppercase(aCellStr
);
352 bFound
= ScGlobal::GetTransliteration().isEqual(aCellStr
, aUpper
);
356 SCCOL nField
= aLook
.Col();
358 return bFound
? nField
: -1;
361 std::unique_ptr
<ScDBQueryParamBase
> ScDBInternalRange::createQueryParam(const ScDBRangeBase
* pQueryRef
) const
363 unique_ptr
<ScDBQueryParamInternal
> pParam(new ScDBQueryParamInternal
);
365 // Set the database range first.
366 const ScAddress
& s
= maRange
.aStart
;
367 const ScAddress
& e
= maRange
.aEnd
;
368 pParam
->nCol1
= s
.Col();
369 pParam
->nRow1
= s
.Row();
370 pParam
->nCol2
= e
.Col();
371 pParam
->nRow2
= e
.Row();
372 pParam
->nTab
= s
.Tab();
374 fillQueryOptions(pParam
.get());
376 // Now construct the query entries from the query range.
377 if (!pQueryRef
->fillQueryEntries(pParam
.get(), this))
380 return std::unique_ptr
<ScDBQueryParamBase
>(std::move(pParam
));
383 bool ScDBInternalRange::isRangeEqual(const ScRange
& rRange
) const
385 return maRange
== rRange
;
388 ScDBExternalRange::ScDBExternalRange(ScDocument
* pDoc
, ScMatrixRef pMat
) :
389 ScDBRangeBase(pDoc
), mpMatrix(std::move(pMat
))
392 mpMatrix
->GetDimensions(nC
, nR
);
393 mnCols
= static_cast<SCCOL
>(nC
);
394 mnRows
= static_cast<SCROW
>(nR
);
397 ScDBExternalRange::~ScDBExternalRange()
401 SCCOL
ScDBExternalRange::getColSize() const
406 SCROW
ScDBExternalRange::getRowSize() const
411 SCSIZE
ScDBExternalRange::getVisibleDataCellCount() const
413 SCCOL nCols
= getColSize();
414 SCROW nRows
= getRowSize();
418 return (nRows
-1)*nCols
;
421 OUString
ScDBExternalRange::getString(SCCOL nCol
, SCROW nRow
) const
423 if (nCol
>= mnCols
|| nRow
>= mnRows
)
426 return mpMatrix
->GetString(nCol
, nRow
).getString();
429 SCCOL
ScDBExternalRange::getFirstFieldColumn() const
434 SCCOL
ScDBExternalRange::findFieldColumn(SCCOL nIndex
) const
439 SCCOL
ScDBExternalRange::findFieldColumn(const OUString
& rStr
, FormulaError
* pErr
) const
442 *pErr
= FormulaError::NONE
;
444 OUString aUpper
= rStr
;
445 lcl_uppercase(aUpper
);
446 for (SCCOL i
= 0; i
< mnCols
; ++i
)
448 OUString aUpperVal
= mpMatrix
->GetString(i
, 0).getString();
449 lcl_uppercase(aUpperVal
);
450 if (aUpper
== aUpperVal
)
456 std::unique_ptr
<ScDBQueryParamBase
> ScDBExternalRange::createQueryParam(const ScDBRangeBase
* pQueryRef
) const
458 unique_ptr
<ScDBQueryParamMatrix
> pParam(new ScDBQueryParamMatrix
);
459 pParam
->mpMatrix
= mpMatrix
;
460 fillQueryOptions(pParam
.get());
462 // Now construct the query entries from the query range.
463 if (!pQueryRef
->fillQueryEntries(pParam
.get(), this))
466 return std::unique_ptr
<ScDBQueryParamBase
>(std::move(pParam
));
469 bool ScDBExternalRange::isRangeEqual(const ScRange
& /*rRange*/) const
474 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */