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/collatorwrapper.hxx>
26 #include <unotools/textsearch.hxx>
27 #include <unotools/transliterationwrapper.hxx>
28 #include <rtl/math.hxx>
29 #include <osl/diagnose.h>
33 Compare::Cell::Cell() :
34 mfValue(0.0), mbValue(false), mbEmpty(false) {}
37 meOp(SC_EQUAL
), mbIgnoreCase(true) {}
39 CompareOptions::CompareOptions( const ScDocument
& rDoc
, const ScQueryEntry
& rEntry
, utl::SearchParam::SearchType eSrchTyp
) :
41 eSearchType(eSrchTyp
),
42 bMatchWholeCell(rDoc
.GetDocOptions().IsMatchWholeCell())
44 // Wildcard and Regex search work only with equal or not equal.
45 if (eSearchType
!= utl::SearchParam::SearchType::Normal
&&
46 aQueryEntry
.eOp
!= SC_EQUAL
&& aQueryEntry
.eOp
!= SC_NOT_EQUAL
)
47 eSearchType
= utl::SearchParam::SearchType::Normal
;
49 // Interpreter functions usually are case insensitive, except the simple
50 // comparison operators, for which these options aren't used. Override in
54 double CompareFunc( const Compare
& rComp
, CompareOptions
* pOptions
)
56 const Compare::Cell
& rCell1
= rComp
.maCells
[0];
57 const Compare::Cell
& rCell2
= rComp
.maCells
[1];
59 // Keep DoubleError if encountered
60 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
61 if (!rCell1
.mbEmpty
&& rCell1
.mbValue
&& !std::isfinite(rCell1
.mfValue
))
62 return rCell1
.mfValue
;
63 if (!rCell2
.mbEmpty
&& rCell2
.mbValue
&& !std::isfinite(rCell2
.mfValue
))
64 return rCell2
.mfValue
;
66 size_t nStringQuery
= 0; // 0:=no, 1:=0, 2:=1
71 ; // empty cell == empty cell, fRes 0
72 else if (rCell2
.mbValue
)
74 if (rCell2
.mfValue
!= 0.0)
76 if (rCell2
.mfValue
< 0.0)
77 fRes
= 1; // empty cell > -x
79 fRes
= -1; // empty cell < x
81 // else: empty cell == 0.0
85 if (!rCell2
.maStr
.isEmpty())
86 fRes
= -1; // empty cell < "..."
87 // else: empty cell == ""
90 else if (rCell2
.mbEmpty
)
94 if (rCell1
.mfValue
!= 0.0)
96 if (rCell1
.mfValue
< 0.0)
97 fRes
= -1; // -x < empty cell
99 fRes
= 1; // x > empty cell
101 // else: empty cell == 0.0
105 if (!rCell1
.maStr
.isEmpty())
106 fRes
= 1; // "..." > empty cell
107 // else: "" == empty cell
110 else if (rCell1
.mbValue
)
114 if (!rtl::math::approxEqual(rCell1
.mfValue
, rCell2
.mfValue
))
116 if (rCell1
.mfValue
- rCell2
.mfValue
< 0)
124 fRes
= -1; // number is less than string
125 nStringQuery
= 2; // 1+1
128 else if (rCell2
.mbValue
)
130 fRes
= 1; // string is greater than number
131 nStringQuery
= 1; // 0+1
138 // All similar to ScTable::ValidQuery(), *rComp.pVal[1] actually
139 // is/must be identical to *rEntry.pStr, which is essential for
140 // regex to work through GetSearchTextPtr().
141 ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
142 OSL_ENSURE(rEntry
.GetQueryItem().maString
== rCell2
.maStr
, "ScInterpreter::CompareFunc: broken options");
143 if (pOptions
->eSearchType
!= utl::SearchParam::SearchType::Normal
)
145 sal_Int32 nStart
= 0;
146 sal_Int32 nStop
= rCell1
.maStr
.getLength();
147 bool bMatch
= rEntry
.GetSearchTextPtr( pOptions
->eSearchType
, !rComp
.mbIgnoreCase
,
148 pOptions
->bMatchWholeCell
)->SearchForward( rCell1
.maStr
.getString(), &nStart
, &nStop
);
149 if (bMatch
&& pOptions
->bMatchWholeCell
&& (nStart
!= 0 || nStop
!= rCell1
.maStr
.getLength()))
150 bMatch
= false; // RegEx must match entire string.
151 fRes
= (bMatch
? 0 : 1);
153 else if (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
)
155 ::utl::TransliterationWrapper
& rTransliteration
=
156 ScGlobal::GetTransliteration(!rComp
.mbIgnoreCase
);
158 if (pOptions
->bMatchWholeCell
)
160 if (rComp
.mbIgnoreCase
)
161 bMatch
= rCell1
.maStr
.getDataIgnoreCase() == rCell2
.maStr
.getDataIgnoreCase();
163 bMatch
= rCell1
.maStr
.getData() == rCell2
.maStr
.getData();
167 const LanguageType nLang
= ScGlobal::oSysLocale
->GetLanguageTag().getLanguageType();
168 OUString
aCell( rTransliteration
.transliterate(
169 rCell1
.maStr
.getString(), nLang
, 0,
170 rCell1
.maStr
.getLength(), nullptr));
171 OUString
aQuer( rTransliteration
.transliterate(
172 rCell2
.maStr
.getString(), nLang
, 0,
173 rCell2
.maStr
.getLength(), nullptr));
174 bMatch
= (aCell
.indexOf( aQuer
) != -1);
176 fRes
= (bMatch
? 0 : 1);
178 else if (rComp
.mbIgnoreCase
)
179 fRes
= static_cast<double>(ScGlobal::GetCollator().compareString(
180 rCell1
.maStr
.getString(), rCell2
.maStr
.getString()));
182 fRes
= static_cast<double>(ScGlobal::GetCaseCollator().compareString(
183 rCell1
.maStr
.getString(), rCell2
.maStr
.getString()));
185 else if (rComp
.meOp
== SC_EQUAL
|| rComp
.meOp
== SC_NOT_EQUAL
)
187 if (rComp
.mbIgnoreCase
)
188 fRes
= (rCell1
.maStr
.getDataIgnoreCase() == rCell2
.maStr
.getDataIgnoreCase()) ? 0 : 1;
190 fRes
= (rCell1
.maStr
.getData() == rCell2
.maStr
.getData()) ? 0 : 1;
192 else if (rComp
.mbIgnoreCase
)
193 fRes
= static_cast<double>(ScGlobal::GetCollator().compareString(
194 rCell1
.maStr
.getString(), rCell2
.maStr
.getString()));
196 fRes
= static_cast<double>(ScGlobal::GetCaseCollator().compareString(
197 rCell1
.maStr
.getString(), rCell2
.maStr
.getString()));
200 if (nStringQuery
&& pOptions
)
202 const ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
203 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
206 const ScQueryEntry::Item
& rItem
= rItems
[0];
207 if (rItem
.meType
!= ScQueryEntry::ByString
&& !rItem
.maString
.isEmpty() &&
208 (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
))
210 // As in ScTable::ValidQuery() match a numeric string for a
211 // number query that originated from a string, e.g. in SUMIF
212 // and COUNTIF. Transliteration is not needed here.
214 if (nStringQuery
== 1)
215 bEqual
= rCell1
.maStr
== rItem
.maString
;
217 bEqual
= rCell2
.maStr
== rItem
.maString
;
219 // match => fRes=0, else fRes=1
220 fRes
= double((rEntry
.eOp
== SC_NOT_EQUAL
) ? bEqual
: !bEqual
);
228 double CompareFunc( const Compare::Cell
& rCell1
, double fCell2
, const CompareOptions
* pOptions
)
230 // Keep DoubleError if encountered
231 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
232 if (!rCell1
.mbEmpty
&& rCell1
.mbValue
&& !std::isfinite(rCell1
.mfValue
))
233 return rCell1
.mfValue
;
234 if (!std::isfinite(fCell2
))
237 bool bStringQuery
= false;
244 fRes
= 1; // empty cell > -x
246 fRes
= -1; // empty cell < x
248 // else: empty cell == 0.0
250 else if (rCell1
.mbValue
)
252 if (!rtl::math::approxEqual(rCell1
.mfValue
, fCell2
))
254 if (rCell1
.mfValue
- fCell2
< 0)
262 fRes
= 1; // string is greater than number
266 if (bStringQuery
&& pOptions
)
268 const ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
269 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
272 const ScQueryEntry::Item
& rItem
= rItems
[0];
273 if (rItem
.meType
!= ScQueryEntry::ByString
&& !rItem
.maString
.isEmpty() &&
274 (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
))
276 // As in ScTable::ValidQuery() match a numeric string for a
277 // number query that originated from a string, e.g. in SUMIF
278 // and COUNTIF. Transliteration is not needed here.
279 bool bEqual
= rCell1
.maStr
== rItem
.maString
;
281 // match => fRes=0, else fRes=1
282 fRes
= double((rEntry
.eOp
== SC_NOT_EQUAL
) ? bEqual
: !bEqual
);
290 double CompareFunc( double fCell1
, double fCell2
)
292 // Keep DoubleError if encountered
293 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
294 if (!std::isfinite(fCell1
))
296 if (!std::isfinite(fCell2
))
301 if (!rtl::math::approxEqual(fCell1
, fCell2
))
303 if (fCell1
- fCell2
< 0.0)
312 double CompareEmptyToNumericFunc( double fCell2
)
314 // Keep DoubleError if encountered
315 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
316 if (!std::isfinite(fCell2
))
323 fRes
= 1; // empty cell > -x
325 fRes
= -1; // empty cell < x
327 // else: empty cell == 0.0
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */