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"
34 using ::std::auto_ptr
;
39 void lcl_uppercase(OUString
& rStr
)
41 rStr
= ScGlobal::pCharClass
->uppercase(rStr
.trim());
44 bool lcl_createStarQuery(
45 svl::SharedStringPool
& rPool
, ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
, const ScDBRangeBase
* pQueryRef
)
47 // A valid StarQuery must be at least 4 columns wide. To be precise it
48 // should be exactly 4 columns ...
49 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
50 // column Excel style query range immediately left to itself would result
51 // in a circular reference when the field name or operator or value (first
52 // to third query range column) is obtained (#i58354#). Furthermore, if the
53 // range wasn't sufficiently specified data changes wouldn't flag formula
54 // cells for recalculation.
56 if (pQueryRef
->getColSize() < 4)
63 SCROW nRows
= pDBRef
->getRowSize();
64 SCSIZE nNewEntries
= static_cast<SCSIZE
>(nRows
);
65 pParam
->Resize(nNewEntries
);
69 ScQueryEntry
& rEntry
= pParam
->GetEntry(nIndex
);
75 // For all entries after the first one, check the and/or connector in the first column.
76 aCellStr
= pQueryRef
->getString(0, nRow
);
77 lcl_uppercase(aCellStr
);
78 if ( aCellStr
.equals(ScGlobal::GetRscString(STR_TABLE_UND
)) )
80 rEntry
.eConnect
= SC_AND
;
83 else if ( aCellStr
.equals(ScGlobal::GetRscString(STR_TABLE_ODER
)) )
85 rEntry
.eConnect
= SC_OR
;
90 if ((nIndex
< 1) || bValid
)
92 // field name in the 2nd column.
93 aCellStr
= pQueryRef
->getString(1, nRow
);
94 SCCOL nField
= pDBRef
->findFieldColumn(aCellStr
); // TODO: must be case insensitive comparison.
97 rEntry
.nField
= nField
;
106 // equality, non-equality operator in the 3rd column.
107 aCellStr
= pQueryRef
->getString(2, nRow
);
108 lcl_uppercase(aCellStr
);
109 const sal_Unicode
* p
= aCellStr
.getStr();
113 rEntry
.eOp
= SC_NOT_EQUAL
;
114 else if (p
[1] == '=')
115 rEntry
.eOp
= SC_LESS_EQUAL
;
117 rEntry
.eOp
= SC_LESS
;
119 else if (p
[0] == '>')
122 rEntry
.eOp
= SC_GREATER_EQUAL
;
124 rEntry
.eOp
= SC_GREATER
;
126 else if (p
[0] == '=')
127 rEntry
.eOp
= SC_EQUAL
;
133 // Finally, the right-hand-side value in the 4th column.
134 rEntry
.GetQueryItem().maString
=
135 rPool
.intern(pQueryRef
->getString(3, nRow
));
136 rEntry
.bDoQuery
= true;
141 while (bValid
&& (nRow
< nRows
) /* && (nIndex < MAXQUERY) */ );
145 bool lcl_createExcelQuery(
146 svl::SharedStringPool
& rPool
, ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
, const ScDBRangeBase
* pQueryRef
)
149 SCCOL nCols
= pQueryRef
->getColSize();
150 SCROW nRows
= pQueryRef
->getRowSize();
151 vector
<SCCOL
> aFields(nCols
);
153 while (bValid
&& (nCol
< nCols
))
155 OUString aQueryStr
= pQueryRef
->getString(nCol
, 0);
156 SCCOL nField
= pDBRef
->findFieldColumn(aQueryStr
);
157 if (ValidCol(nField
))
158 aFields
[nCol
] = nField
;
166 // Count the number of visible cells (excluding the header row). Each
167 // visible cell corresponds with a single query.
168 SCSIZE nVisible
= pQueryRef
->getVisibleDataCellCount();
169 if ( nVisible
> SCSIZE_MAX
/ sizeof(void*) )
171 OSL_FAIL("too many filter criteria");
175 SCSIZE nNewEntries
= nVisible
;
176 pParam
->Resize( nNewEntries
);
186 aCellStr
= pQueryRef
->getString(nCol
, nRow
);
187 aCellStr
= ScGlobal::pCharClass
->uppercase( aCellStr
);
188 if (!aCellStr
.isEmpty())
190 if (nIndex
< nNewEntries
)
192 pParam
->GetEntry(nIndex
).nField
= aFields
[nCol
];
193 pParam
->FillInExcelSyntax(rPool
, aCellStr
, nIndex
);
195 if (nIndex
< nNewEntries
)
196 pParam
->GetEntry(nIndex
).eConnect
= SC_AND
;
204 if (nIndex
< nNewEntries
)
205 pParam
->GetEntry(nIndex
).eConnect
= SC_OR
;
211 bool lcl_fillQueryEntries(
212 svl::SharedStringPool
& rPool
, ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
, const ScDBRangeBase
* pQueryRef
)
214 SCSIZE nCount
= pParam
->GetEntryCount();
215 for (SCSIZE i
= 0; i
< nCount
; ++i
)
216 pParam
->GetEntry(i
).Clear();
218 // Standard QueryTabelle
219 bool bValid
= lcl_createStarQuery(rPool
, pParam
, pDBRef
, pQueryRef
);
220 // Excel QueryTabelle
222 bValid
= lcl_createExcelQuery(rPool
, pParam
, pDBRef
, pQueryRef
);
224 nCount
= pParam
->GetEntryCount();
227 // bQueryByString muss gesetzt sein
228 for (SCSIZE i
= 0; i
< nCount
; ++i
)
229 pParam
->GetEntry(i
).GetQueryItem().meType
= ScQueryEntry::ByString
;
234 for (SCSIZE i
= 0; i
< nCount
; ++i
)
235 pParam
->GetEntry(i
).Clear();
242 ScDBRangeBase::ScDBRangeBase(ScDocument
* pDoc
, RefType eType
) :
243 mpDoc(pDoc
), meType(eType
)
247 ScDBRangeBase::~ScDBRangeBase()
251 bool ScDBRangeBase::fillQueryEntries(ScQueryParamBase
* pParam
, const ScDBRangeBase
* pDBRef
) const
256 return lcl_fillQueryEntries(getDoc()->GetSharedStringPool(), pParam
, pDBRef
, this);
259 void ScDBRangeBase::fillQueryOptions(ScQueryParamBase
* pParam
)
261 pParam
->bHasHeader
= true;
262 pParam
->bByRow
= true;
263 pParam
->bInplace
= true;
264 pParam
->bCaseSens
= false;
265 pParam
->bRegExp
= false;
266 pParam
->bDuplicate
= true;
269 ScDocument
* ScDBRangeBase::getDoc() const
274 ScDBInternalRange::ScDBInternalRange(ScDocument
* pDoc
, const ScRange
& rRange
) :
275 ScDBRangeBase(pDoc
, INTERNAL
), maRange(rRange
)
279 ScDBInternalRange::~ScDBInternalRange()
283 const ScRange
& ScDBInternalRange::getRange() const
288 SCCOL
ScDBInternalRange::getColSize() const
290 return maRange
.aEnd
.Col() - maRange
.aStart
.Col() + 1;
293 SCROW
ScDBInternalRange::getRowSize() const
295 return maRange
.aEnd
.Row() - maRange
.aStart
.Row() + 1;
298 SCSIZE
ScDBInternalRange::getVisibleDataCellCount() const
300 SCCOL nCols
= getColSize();
301 SCROW nRows
= getRowSize();
305 return (nRows
-1)*nCols
;
308 OUString
ScDBInternalRange::getString(SCCOL nCol
, SCROW nRow
) const
311 const ScAddress
& s
= maRange
.aStart
;
312 // #i109200# this is used in formula calculation, use GetInputString, not GetString
313 // (consistent with ScDBInternalRange::getCellString)
314 // GetStringForFormula is not used here, to allow querying for date values.
315 getDoc()->GetInputString(s
.Col() + nCol
, s
.Row() + nRow
, maRange
.aStart
.Tab(), aStr
);
319 SCCOL
ScDBInternalRange::getFirstFieldColumn() const
321 return getRange().aStart
.Col();
324 SCCOL
ScDBInternalRange::findFieldColumn(SCCOL nIndex
) const
326 const ScRange
& rRange
= getRange();
327 const ScAddress
& s
= rRange
.aStart
;
329 SCCOL nDBCol1
= s
.Col();
331 // Don't handle out-of-bound condition here. We'll do that later.
332 return nIndex
+ nDBCol1
- 1;
335 SCCOL
ScDBInternalRange::findFieldColumn(const OUString
& rStr
, sal_uInt16
* pErr
) const
337 const ScAddress
& s
= maRange
.aStart
;
338 const ScAddress
& e
= maRange
.aEnd
;
339 OUString aUpper
= rStr
;
340 lcl_uppercase(aUpper
);
342 SCCOL nDBCol1
= s
.Col();
343 SCROW nDBRow1
= s
.Row();
344 SCTAB nDBTab1
= s
.Tab();
345 SCCOL nDBCol2
= e
.Col();
347 SCCOL nField
= nDBCol1
;
348 sal_Bool bFound
= sal_False
;
351 ScAddress
aLook( nDBCol1
, nDBRow1
, nDBTab1
);
352 while (!bFound
&& (aLook
.Col() <= nDBCol2
))
354 sal_uInt16 nErr
= getDoc()->GetStringForFormula( aLook
, aCellStr
);
357 lcl_uppercase(aCellStr
);
358 bFound
= ScGlobal::GetpTransliteration()->isEqual(aCellStr
, aUpper
);
362 nField
= aLook
.Col();
364 return bFound
? nField
: -1;
367 ScDBQueryParamBase
* ScDBInternalRange::createQueryParam(const ScDBRangeBase
* pQueryRef
) const
369 auto_ptr
<ScDBQueryParamInternal
> pParam(new ScDBQueryParamInternal
);
371 // Set the database range first.
372 const ScAddress
& s
= maRange
.aStart
;
373 const ScAddress
& e
= maRange
.aEnd
;
374 pParam
->nCol1
= s
.Col();
375 pParam
->nRow1
= s
.Row();
376 pParam
->nCol2
= e
.Col();
377 pParam
->nRow2
= e
.Row();
378 pParam
->nTab
= s
.Tab();
380 fillQueryOptions(pParam
.get());
382 // Now construct the query entries from the query range.
383 if (!pQueryRef
->fillQueryEntries(pParam
.get(), this))
386 return pParam
.release();
389 bool ScDBInternalRange::isRangeEqual(const ScRange
& rRange
) const
391 return maRange
== rRange
;
394 ScDBExternalRange::ScDBExternalRange(ScDocument
* pDoc
, const ScMatrixRef
& pMat
) :
395 ScDBRangeBase(pDoc
, EXTERNAL
), mpMatrix(pMat
)
398 mpMatrix
->GetDimensions(nC
, nR
);
399 mnCols
= static_cast<SCCOL
>(nC
);
400 mnRows
= static_cast<SCROW
>(nR
);
403 ScDBExternalRange::~ScDBExternalRange()
407 SCCOL
ScDBExternalRange::getColSize() const
412 SCROW
ScDBExternalRange::getRowSize() const
417 SCSIZE
ScDBExternalRange::getVisibleDataCellCount() const
419 SCCOL nCols
= getColSize();
420 SCROW nRows
= getRowSize();
424 return (nRows
-1)*nCols
;
427 OUString
ScDBExternalRange::getString(SCCOL nCol
, SCROW nRow
) const
429 if (nCol
>= mnCols
|| nRow
>= mnRows
)
432 return mpMatrix
->GetString(nCol
, nRow
).getString();
435 SCCOL
ScDBExternalRange::getFirstFieldColumn() const
440 SCCOL
ScDBExternalRange::findFieldColumn(SCCOL nIndex
) const
445 SCCOL
ScDBExternalRange::findFieldColumn(const OUString
& rStr
, sal_uInt16
* pErr
) const
450 OUString aUpper
= rStr
;
451 lcl_uppercase(aUpper
);
452 for (SCCOL i
= 0; i
< mnCols
; ++i
)
454 OUString aUpperVal
= mpMatrix
->GetString(i
, 0).getString();
455 lcl_uppercase(aUpperVal
);
456 if (aUpper
.equals(aUpperVal
))
462 ScDBQueryParamBase
* ScDBExternalRange::createQueryParam(const ScDBRangeBase
* pQueryRef
) const
464 auto_ptr
<ScDBQueryParamMatrix
> pParam(new ScDBQueryParamMatrix
);
465 pParam
->mpMatrix
= mpMatrix
;
466 fillQueryOptions(pParam
.get());
468 // Now construct the query entries from the query range.
469 if (!pQueryRef
->fillQueryEntries(pParam
.get(), this))
472 return pParam
.release();
475 bool ScDBExternalRange::isRangeEqual(const ScRange
& /*rRange*/) const
480 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */