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"
21 #include "formulacell.hxx"
23 #include "document.hxx"
24 #include "queryparam.hxx"
25 #include "queryentry.hxx"
26 #include "globstr.hrc"
27 #include "scmatrix.hxx"
29 #include <svl/sharedstringpool.hxx>
30 #include <osl/diagnose.h>
35 using ::std::unique_ptr
;
40 void lcl_uppercase(OUString
& rStr
)
42 rStr
= ScGlobal::pCharClass
->uppercase(rStr
.trim());
45 bool lcl_createStarQuery(
46 svl::SharedStringPool
& rPool
, ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
, const ScDBRangeBase
* pQueryRef
)
48 // A valid StarQuery must be at least 4 columns wide. To be precise it
49 // should be exactly 4 columns ...
50 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
51 // column Excel style query range immediately left to itself would result
52 // in a circular reference when the field name or operator or value (first
53 // to third query range column) is obtained (#i58354#). Furthermore, if the
54 // range wasn't sufficiently specified data changes wouldn't flag formula
55 // cells for recalculation.
57 if (pQueryRef
->getColSize() < 4)
64 SCROW nRows
= pDBRef
->getRowSize();
65 SCSIZE nNewEntries
= static_cast<SCSIZE
>(nRows
);
66 pParam
->Resize(nNewEntries
);
70 ScQueryEntry
& rEntry
= pParam
->GetEntry(nIndex
);
76 // For all entries after the first one, check the and/or connector in the first column.
77 aCellStr
= pQueryRef
->getString(0, nRow
);
78 lcl_uppercase(aCellStr
);
79 if ( aCellStr
.equals(ScGlobal::GetRscString(STR_TABLE_UND
)) )
81 rEntry
.eConnect
= SC_AND
;
84 else if ( aCellStr
.equals(ScGlobal::GetRscString(STR_TABLE_ODER
)) )
86 rEntry
.eConnect
= SC_OR
;
91 if ((nIndex
< 1) || bValid
)
93 // field name in the 2nd column.
94 aCellStr
= pQueryRef
->getString(1, nRow
);
95 SCCOL nField
= pDBRef
->findFieldColumn(aCellStr
); // TODO: must be case insensitive comparison.
98 rEntry
.nField
= nField
;
107 // equality, non-equality operator in the 3rd column.
108 aCellStr
= pQueryRef
->getString(2, nRow
);
109 lcl_uppercase(aCellStr
);
110 const sal_Unicode
* p
= aCellStr
.getStr();
114 rEntry
.eOp
= SC_NOT_EQUAL
;
115 else if (p
[1] == '=')
116 rEntry
.eOp
= SC_LESS_EQUAL
;
118 rEntry
.eOp
= SC_LESS
;
120 else if (p
[0] == '>')
123 rEntry
.eOp
= SC_GREATER_EQUAL
;
125 rEntry
.eOp
= SC_GREATER
;
127 else if (p
[0] == '=')
128 rEntry
.eOp
= SC_EQUAL
;
134 // Finally, the right-hand-side value in the 4th column.
135 rEntry
.GetQueryItem().maString
=
136 rPool
.intern(pQueryRef
->getString(3, nRow
));
137 rEntry
.bDoQuery
= true;
142 while (bValid
&& (nRow
< nRows
) /* && (nIndex < MAXQUERY) */ );
146 bool lcl_createExcelQuery(
147 svl::SharedStringPool
& rPool
, ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
, const ScDBRangeBase
* pQueryRef
)
150 SCCOL nCols
= pQueryRef
->getColSize();
151 SCROW nRows
= pQueryRef
->getRowSize();
152 vector
<SCCOL
> aFields(nCols
);
154 while (bValid
&& (nCol
< nCols
))
156 OUString aQueryStr
= pQueryRef
->getString(nCol
, 0);
157 SCCOL nField
= pDBRef
->findFieldColumn(aQueryStr
);
158 if (ValidCol(nField
))
159 aFields
[nCol
] = nField
;
167 // Count the number of visible cells (excluding the header row). Each
168 // visible cell corresponds with a single query.
169 SCSIZE nVisible
= pQueryRef
->getVisibleDataCellCount();
170 if ( nVisible
> SCSIZE_MAX
/ sizeof(void*) )
172 OSL_FAIL("too many filter criteria");
176 SCSIZE nNewEntries
= nVisible
;
177 pParam
->Resize( nNewEntries
);
187 aCellStr
= pQueryRef
->getString(nCol
, nRow
);
188 aCellStr
= ScGlobal::pCharClass
->uppercase( aCellStr
);
189 if (!aCellStr
.isEmpty())
191 if (nIndex
< nNewEntries
)
193 pParam
->GetEntry(nIndex
).nField
= aFields
[nCol
];
194 pParam
->FillInExcelSyntax(rPool
, aCellStr
, nIndex
, NULL
);
196 if (nIndex
< nNewEntries
)
197 pParam
->GetEntry(nIndex
).eConnect
= SC_AND
;
205 if (nIndex
< nNewEntries
)
206 pParam
->GetEntry(nIndex
).eConnect
= SC_OR
;
212 bool lcl_fillQueryEntries(
213 svl::SharedStringPool
& rPool
, ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
, const ScDBRangeBase
* pQueryRef
)
215 SCSIZE nCount
= pParam
->GetEntryCount();
216 for (SCSIZE i
= 0; i
< nCount
; ++i
)
217 pParam
->GetEntry(i
).Clear();
219 // Standard QueryTabelle
220 bool bValid
= lcl_createStarQuery(rPool
, pParam
, pDBRef
, pQueryRef
);
221 // Excel QueryTabelle
223 bValid
= lcl_createExcelQuery(rPool
, pParam
, pDBRef
, pQueryRef
);
225 nCount
= pParam
->GetEntryCount();
228 // bQueryByString muss gesetzt sein
229 for (SCSIZE i
= 0; i
< nCount
; ++i
)
230 pParam
->GetEntry(i
).GetQueryItem().meType
= ScQueryEntry::ByString
;
235 for (SCSIZE i
= 0; i
< nCount
; ++i
)
236 pParam
->GetEntry(i
).Clear();
243 ScDBRangeBase::ScDBRangeBase(ScDocument
* pDoc
, RefType eType
) :
244 mpDoc(pDoc
), meType(eType
)
248 ScDBRangeBase::~ScDBRangeBase()
252 bool ScDBRangeBase::fillQueryEntries(ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
) const
257 return lcl_fillQueryEntries(getDoc()->GetSharedStringPool(), pParam
, pDBRef
, this);
260 void ScDBRangeBase::fillQueryOptions(ScQueryParamBase
* pParam
)
262 pParam
->bHasHeader
= true;
263 pParam
->bByRow
= true;
264 pParam
->bInplace
= true;
265 pParam
->bCaseSens
= false;
266 pParam
->bRegExp
= false;
267 pParam
->bDuplicate
= true;
270 ScDBInternalRange::ScDBInternalRange(ScDocument
* pDoc
, const ScRange
& rRange
) :
271 ScDBRangeBase(pDoc
, INTERNAL
), maRange(rRange
)
275 ScDBInternalRange::~ScDBInternalRange()
279 SCCOL
ScDBInternalRange::getColSize() const
281 return maRange
.aEnd
.Col() - maRange
.aStart
.Col() + 1;
284 SCROW
ScDBInternalRange::getRowSize() const
286 return maRange
.aEnd
.Row() - maRange
.aStart
.Row() + 1;
289 SCSIZE
ScDBInternalRange::getVisibleDataCellCount() const
291 SCCOL nCols
= getColSize();
292 SCROW nRows
= getRowSize();
296 return (nRows
-1)*nCols
;
299 OUString
ScDBInternalRange::getString(SCCOL nCol
, SCROW nRow
) const
302 const ScAddress
& s
= maRange
.aStart
;
303 // #i109200# this is used in formula calculation, use GetInputString, not GetString
304 // (consistent with ScDBInternalRange::getCellString)
305 // GetStringForFormula is not used here, to allow querying for date values.
306 getDoc()->GetInputString(s
.Col() + nCol
, s
.Row() + nRow
, maRange
.aStart
.Tab(), aStr
);
310 SCCOL
ScDBInternalRange::getFirstFieldColumn() const
312 return getRange().aStart
.Col();
315 SCCOL
ScDBInternalRange::findFieldColumn(SCCOL nIndex
) const
317 const ScRange
& rRange
= getRange();
318 const ScAddress
& s
= rRange
.aStart
;
320 SCCOL nDBCol1
= s
.Col();
322 // Don't handle out-of-bound condition here. We'll do that later.
323 return nIndex
+ nDBCol1
- 1;
326 SCCOL
ScDBInternalRange::findFieldColumn(const OUString
& rStr
, sal_uInt16
* pErr
) const
328 const ScAddress
& s
= maRange
.aStart
;
329 const ScAddress
& e
= maRange
.aEnd
;
330 OUString aUpper
= rStr
;
331 lcl_uppercase(aUpper
);
333 SCCOL nDBCol1
= s
.Col();
334 SCROW nDBRow1
= s
.Row();
335 SCTAB nDBTab1
= s
.Tab();
336 SCCOL nDBCol2
= e
.Col();
338 SCCOL nField
= nDBCol1
;
342 ScAddress
aLook( nDBCol1
, nDBRow1
, nDBTab1
);
343 while (!bFound
&& (aLook
.Col() <= nDBCol2
))
345 sal_uInt16 nErr
= getDoc()->GetStringForFormula( aLook
, aCellStr
);
348 lcl_uppercase(aCellStr
);
349 bFound
= ScGlobal::GetpTransliteration()->isEqual(aCellStr
, aUpper
);
353 nField
= aLook
.Col();
355 return bFound
? nField
: -1;
358 ScDBQueryParamBase
* ScDBInternalRange::createQueryParam(const ScDBRangeBase
* pQueryRef
) const
360 unique_ptr
<ScDBQueryParamInternal
> pParam(new ScDBQueryParamInternal
);
362 // Set the database range first.
363 const ScAddress
& s
= maRange
.aStart
;
364 const ScAddress
& e
= maRange
.aEnd
;
365 pParam
->nCol1
= s
.Col();
366 pParam
->nRow1
= s
.Row();
367 pParam
->nCol2
= e
.Col();
368 pParam
->nRow2
= e
.Row();
369 pParam
->nTab
= s
.Tab();
371 fillQueryOptions(pParam
.get());
373 // Now construct the query entries from the query range.
374 if (!pQueryRef
->fillQueryEntries(pParam
.get(), this))
377 return pParam
.release();
380 bool ScDBInternalRange::isRangeEqual(const ScRange
& rRange
) const
382 return maRange
== rRange
;
385 ScDBExternalRange::ScDBExternalRange(ScDocument
* pDoc
, const ScMatrixRef
& pMat
) :
386 ScDBRangeBase(pDoc
, EXTERNAL
), mpMatrix(pMat
)
389 mpMatrix
->GetDimensions(nC
, nR
);
390 mnCols
= static_cast<SCCOL
>(nC
);
391 mnRows
= static_cast<SCROW
>(nR
);
394 ScDBExternalRange::~ScDBExternalRange()
398 SCCOL
ScDBExternalRange::getColSize() const
403 SCROW
ScDBExternalRange::getRowSize() const
408 SCSIZE
ScDBExternalRange::getVisibleDataCellCount() const
410 SCCOL nCols
= getColSize();
411 SCROW nRows
= getRowSize();
415 return (nRows
-1)*nCols
;
418 OUString
ScDBExternalRange::getString(SCCOL nCol
, SCROW nRow
) const
420 if (nCol
>= mnCols
|| nRow
>= mnRows
)
423 return mpMatrix
->GetString(nCol
, nRow
).getString();
426 SCCOL
ScDBExternalRange::getFirstFieldColumn() const
431 SCCOL
ScDBExternalRange::findFieldColumn(SCCOL nIndex
) const
436 SCCOL
ScDBExternalRange::findFieldColumn(const OUString
& rStr
, sal_uInt16
* pErr
) const
441 OUString aUpper
= rStr
;
442 lcl_uppercase(aUpper
);
443 for (SCCOL i
= 0; i
< mnCols
; ++i
)
445 OUString aUpperVal
= mpMatrix
->GetString(i
, 0).getString();
446 lcl_uppercase(aUpperVal
);
447 if (aUpper
.equals(aUpperVal
))
453 ScDBQueryParamBase
* ScDBExternalRange::createQueryParam(const ScDBRangeBase
* pQueryRef
) const
455 unique_ptr
<ScDBQueryParamMatrix
> pParam(new ScDBQueryParamMatrix
);
456 pParam
->mpMatrix
= mpMatrix
;
457 fillQueryOptions(pParam
.get());
459 // Now construct the query entries from the query range.
460 if (!pQueryRef
->fillQueryEntries(pParam
.get(), this))
463 return pParam
.release();
466 bool ScDBExternalRange::isRangeEqual(const ScRange
& /*rRange*/) const
471 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */