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 "compare.hxx"
22 #include "document.hxx"
23 #include "docoptio.hxx"
25 #include "unotools/textsearch.hxx"
29 Compare::Cell::Cell() :
30 mfValue(0.0), mbValue(false), mbEmpty(false) {}
33 meOp(SC_EQUAL
), mbIgnoreCase(true) {}
35 CompareOptions::CompareOptions( ScDocument
* pDoc
, const ScQueryEntry
& rEntry
, bool bReg
) :
38 bMatchWholeCell(pDoc
->GetDocOptions().IsMatchWholeCell())
40 bRegEx
= (bRegEx
&& (aQueryEntry
.eOp
== SC_EQUAL
|| aQueryEntry
.eOp
== SC_NOT_EQUAL
));
41 // Interpreter functions usually are case insensitive, except the simple
42 // comparison operators, for which these options aren't used. Override in
46 double CompareFunc( const Compare::Cell
& rCell1
, const Compare::Cell
& rCell2
, bool bIgnoreCase
, CompareOptions
* pOptions
)
48 // Keep DoubleError if encountered
49 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
50 if (!rCell1
.mbEmpty
&& rCell1
.mbValue
&& !rtl::math::isFinite(rCell1
.mfValue
))
51 return rCell1
.mfValue
;
52 if (!rCell2
.mbEmpty
&& rCell2
.mbValue
&& !rtl::math::isFinite(rCell2
.mfValue
))
53 return rCell2
.mfValue
;
55 size_t nStringQuery
= 0; // 0:=no, 1:=0, 2:=1
60 ; // empty cell == empty cell, fRes 0
61 else if (rCell2
.mbValue
)
63 if (rCell2
.mfValue
!= 0.0)
65 if (rCell2
.mfValue
< 0.0)
66 fRes
= 1; // empty cell > -x
68 fRes
= -1; // empty cell < x
70 // else: empty cell == 0.0
74 if (!rCell2
.maStr
.isEmpty())
75 fRes
= -1; // empty cell < "..."
76 // else: empty cell == ""
79 else if (rCell2
.mbEmpty
)
83 if (rCell1
.mfValue
!= 0.0)
85 if (rCell1
.mfValue
< 0.0)
86 fRes
= -1; // -x < empty cell
88 fRes
= 1; // x > empty cell
90 // else: empty cell == 0.0
94 if (!rCell1
.maStr
.isEmpty())
95 fRes
= 1; // "..." > empty cell
96 // else: "" == empty cell
99 else if (rCell1
.mbValue
)
103 if (!rtl::math::approxEqual(rCell1
.mfValue
, rCell2
.mfValue
))
105 if (rCell1
.mfValue
- rCell2
.mfValue
< 0)
113 fRes
= -1; // number is less than string
114 nStringQuery
= 2; // 1+1
117 else if (rCell2
.mbValue
)
119 fRes
= 1; // string is greater than number
120 nStringQuery
= 1; // 0+1
127 // All similar to ScTable::ValidQuery(), *rComp.pVal[1] actually
128 // is/must be identical to *rEntry.pStr, which is essential for
129 // regex to work through GetSearchTextPtr().
130 ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
131 OSL_ENSURE(rEntry
.GetQueryItem().maString
== rCell2
.maStr
, "ScInterpreter::CompareFunc: broken options");
132 if (pOptions
->bRegEx
)
134 sal_Int32 nStart
= 0;
135 sal_Int32 nStop
= rCell1
.maStr
.getLength();
136 bool bMatch
= rEntry
.GetSearchTextPtr(
137 !bIgnoreCase
)->SearchForward(
138 rCell1
.maStr
.getString(), &nStart
, &nStop
);
139 if (bMatch
&& pOptions
->bMatchWholeCell
&& (nStart
!= 0 || nStop
!= rCell1
.maStr
.getLength()))
140 bMatch
= false; // RegEx must match entire string.
141 fRes
= (bMatch
? 0 : 1);
143 else if (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
)
145 ::utl::TransliterationWrapper
* pTransliteration
=
146 (bIgnoreCase
? ScGlobal::GetpTransliteration() :
147 ScGlobal::GetCaseTransliteration());
149 if (pOptions
->bMatchWholeCell
)
152 bMatch
= rCell1
.maStr
.getDataIgnoreCase() == rCell2
.maStr
.getDataIgnoreCase();
154 bMatch
= rCell1
.maStr
.getData() == rCell2
.maStr
.getData();
158 OUString
aCell( pTransliteration
->transliterate(
159 rCell1
.maStr
.getString(), ScGlobal::eLnge
, 0,
160 rCell1
.maStr
.getLength(), NULL
));
161 OUString
aQuer( pTransliteration
->transliterate(
162 rCell2
.maStr
.getString(), ScGlobal::eLnge
, 0,
163 rCell2
.maStr
.getLength(), NULL
));
164 bMatch
= (aCell
.indexOf( aQuer
) != -1);
166 fRes
= (bMatch
? 0 : 1);
168 else if (bIgnoreCase
)
169 fRes
= (double) ScGlobal::GetCollator()->compareString(
170 rCell1
.maStr
.getString(), rCell2
.maStr
.getString());
172 fRes
= (double) ScGlobal::GetCaseCollator()->compareString(
173 rCell1
.maStr
.getString(), rCell2
.maStr
.getString());
175 else if (bIgnoreCase
)
176 fRes
= (double) ScGlobal::GetCollator()->compareString(
177 rCell1
.maStr
.getString(), rCell2
.maStr
.getString());
179 fRes
= (double) ScGlobal::GetCaseCollator()->compareString(
180 rCell1
.maStr
.getString(), rCell2
.maStr
.getString());
183 if (nStringQuery
&& pOptions
)
185 const ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
186 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
189 const ScQueryEntry::Item
& rItem
= rItems
[0];
190 if (rItem
.meType
!= ScQueryEntry::ByString
&& !rItem
.maString
.isEmpty() &&
191 (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
))
193 // As in ScTable::ValidQuery() match a numeric string for a
194 // number query that originated from a string, e.g. in SUMIF
195 // and COUNTIF. Transliteration is not needed here.
197 if (nStringQuery
== 1)
198 bEqual
= rCell1
.maStr
== rItem
.maString
;
200 bEqual
= rCell2
.maStr
== rItem
.maString
;
202 // match => fRes=0, else fRes=1
203 fRes
= (rEntry
.eOp
== SC_NOT_EQUAL
) ? bEqual
: !bEqual
;
211 double CompareFunc( double fCell1
, const Compare::Cell
& rCell2
, CompareOptions
* pOptions
)
213 // Keep DoubleError if encountered
214 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
215 if (!rtl::math::isFinite(fCell1
))
217 if (!rCell2
.mbEmpty
&& rCell2
.mbValue
&& !rtl::math::isFinite(rCell2
.mfValue
))
218 return rCell2
.mfValue
;
220 bool bStringQuery
= false;
227 fRes
= -1; // -x < empty cell
229 fRes
= 1; // x > empty cell
231 // else: empty cell == 0.0
237 if (!rtl::math::approxEqual(fCell1
, rCell2
.mfValue
))
239 if (fCell1
- rCell2
.mfValue
< 0)
247 fRes
= -1; // number is less than string
252 if (bStringQuery
&& pOptions
)
254 const ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
255 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
258 const ScQueryEntry::Item
& rItem
= rItems
[0];
259 if (rItem
.meType
!= ScQueryEntry::ByString
&& !rItem
.maString
.isEmpty() &&
260 (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
))
262 // As in ScTable::ValidQuery() match a numeric string for a
263 // number query that originated from a string, e.g. in SUMIF
264 // and COUNTIF. Transliteration is not needed here.
265 bool bEqual
= rCell2
.maStr
== rItem
.maString
;
267 // match => fRes=0, else fRes=1
268 fRes
= (rEntry
.eOp
== SC_NOT_EQUAL
) ? bEqual
: !bEqual
;
276 double CompareFunc( const Compare::Cell
& rCell1
, double fCell2
, CompareOptions
* pOptions
)
278 // Keep DoubleError if encountered
279 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
280 if (!rCell1
.mbEmpty
&& rCell1
.mbValue
&& !rtl::math::isFinite(rCell1
.mfValue
))
281 return rCell1
.mfValue
;
282 if (!rtl::math::isFinite(fCell2
))
285 bool bStringQuery
= false;
292 fRes
= 1; // empty cell > -x
294 fRes
= -1; // empty cell < x
296 // else: empty cell == 0.0
298 else if (rCell1
.mbValue
)
300 if (!rtl::math::approxEqual(rCell1
.mfValue
, fCell2
))
302 if (rCell1
.mfValue
- fCell2
< 0)
310 fRes
= 1; // string is greater than number
314 if (bStringQuery
&& pOptions
)
316 const ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
317 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
320 const ScQueryEntry::Item
& rItem
= rItems
[0];
321 if (rItem
.meType
!= ScQueryEntry::ByString
&& !rItem
.maString
.isEmpty() &&
322 (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
))
324 // As in ScTable::ValidQuery() match a numeric string for a
325 // number query that originated from a string, e.g. in SUMIF
326 // and COUNTIF. Transliteration is not needed here.
327 bool bEqual
= rCell1
.maStr
== rItem
.maString
;
329 // match => fRes=0, else fRes=1
330 fRes
= (rEntry
.eOp
== SC_NOT_EQUAL
) ? bEqual
: !bEqual
;
338 double CompareFunc( double fCell1
, double fCell2
)
340 // Keep DoubleError if encountered
341 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
342 if (!rtl::math::isFinite(fCell1
))
344 if (!rtl::math::isFinite(fCell2
))
349 if (!rtl::math::approxEqual(fCell1
, fCell2
))
351 if (fCell1
- fCell2
< 0.0)
360 double CompareEmptyToNumericFunc( double fCell2
)
362 // Keep DoubleError if encountered
363 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
364 if (!rtl::math::isFinite(fCell2
))
371 fRes
= 1; // empty cell > -x
373 fRes
= -1; // empty cell < x
375 // else: empty cell == 0.0
382 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */