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
& rComp
, CompareOptions
* pOptions
)
48 const Compare::Cell
& rCell1
= rComp
.maCells
[0];
49 const Compare::Cell
& rCell2
= rComp
.maCells
[1];
51 // Keep DoubleError if encountered
52 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
53 if (!rCell1
.mbEmpty
&& rCell1
.mbValue
&& !rtl::math::isFinite(rCell1
.mfValue
))
54 return rCell1
.mfValue
;
55 if (!rCell2
.mbEmpty
&& rCell2
.mbValue
&& !rtl::math::isFinite(rCell2
.mfValue
))
56 return rCell2
.mfValue
;
58 size_t nStringQuery
= 0; // 0:=no, 1:=0, 2:=1
63 ; // empty cell == empty cell, fRes 0
64 else if (rCell2
.mbValue
)
66 if (rCell2
.mfValue
!= 0.0)
68 if (rCell2
.mfValue
< 0.0)
69 fRes
= 1; // empty cell > -x
71 fRes
= -1; // empty cell < x
73 // else: empty cell == 0.0
77 if (!rCell2
.maStr
.isEmpty())
78 fRes
= -1; // empty cell < "..."
79 // else: empty cell == ""
82 else if (rCell2
.mbEmpty
)
86 if (rCell1
.mfValue
!= 0.0)
88 if (rCell1
.mfValue
< 0.0)
89 fRes
= -1; // -x < empty cell
91 fRes
= 1; // x > empty cell
93 // else: empty cell == 0.0
97 if (!rCell1
.maStr
.isEmpty())
98 fRes
= 1; // "..." > empty cell
99 // else: "" == empty cell
102 else if (rCell1
.mbValue
)
106 if (!rtl::math::approxEqual(rCell1
.mfValue
, rCell2
.mfValue
))
108 if (rCell1
.mfValue
- rCell2
.mfValue
< 0)
116 fRes
= -1; // number is less than string
117 nStringQuery
= 2; // 1+1
120 else if (rCell2
.mbValue
)
122 fRes
= 1; // string is greater than number
123 nStringQuery
= 1; // 0+1
130 // All similar to ScTable::ValidQuery(), *rComp.pVal[1] actually
131 // is/must be identical to *rEntry.pStr, which is essential for
132 // regex to work through GetSearchTextPtr().
133 ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
134 OSL_ENSURE(rEntry
.GetQueryItem().maString
== rCell2
.maStr
, "ScInterpreter::CompareFunc: broken options");
135 if (pOptions
->bRegEx
)
137 sal_Int32 nStart
= 0;
138 sal_Int32 nStop
= rCell1
.maStr
.getLength();
139 bool bMatch
= rEntry
.GetSearchTextPtr(
140 !rComp
.mbIgnoreCase
)->SearchForward(
141 rCell1
.maStr
.getString(), &nStart
, &nStop
);
142 if (bMatch
&& pOptions
->bMatchWholeCell
&& (nStart
!= 0 || nStop
!= rCell1
.maStr
.getLength()))
143 bMatch
= false; // RegEx must match entire string.
144 fRes
= (bMatch
? 0 : 1);
146 else if (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
)
148 ::utl::TransliterationWrapper
* pTransliteration
=
149 (rComp
.mbIgnoreCase
? ScGlobal::GetpTransliteration() :
150 ScGlobal::GetCaseTransliteration());
152 if (pOptions
->bMatchWholeCell
)
154 if (rComp
.mbIgnoreCase
)
155 bMatch
= rCell1
.maStr
.getDataIgnoreCase() == rCell2
.maStr
.getDataIgnoreCase();
157 bMatch
= rCell1
.maStr
.getData() == rCell2
.maStr
.getData();
161 OUString
aCell( pTransliteration
->transliterate(
162 rCell1
.maStr
.getString(), ScGlobal::eLnge
, 0,
163 rCell1
.maStr
.getLength(), NULL
));
164 OUString
aQuer( pTransliteration
->transliterate(
165 rCell2
.maStr
.getString(), ScGlobal::eLnge
, 0,
166 rCell2
.maStr
.getLength(), NULL
));
167 bMatch
= (aCell
.indexOf( aQuer
) != -1);
169 fRes
= (bMatch
? 0 : 1);
171 else if (rComp
.mbIgnoreCase
)
172 fRes
= (double) ScGlobal::GetCollator()->compareString(
173 rCell1
.maStr
.getString(), rCell2
.maStr
.getString());
175 fRes
= (double) ScGlobal::GetCaseCollator()->compareString(
176 rCell1
.maStr
.getString(), rCell2
.maStr
.getString());
178 else if (rComp
.meOp
== SC_EQUAL
|| rComp
.meOp
== SC_NOT_EQUAL
)
180 if (rComp
.mbIgnoreCase
)
181 fRes
= (rCell1
.maStr
.getDataIgnoreCase() == rCell2
.maStr
.getDataIgnoreCase()) ? 0 : 1;
183 fRes
= (rCell1
.maStr
.getData() == rCell2
.maStr
.getData()) ? 0 : 1;
185 else if (rComp
.mbIgnoreCase
)
186 fRes
= (double) ScGlobal::GetCollator()->compareString(
187 rCell1
.maStr
.getString(), rCell2
.maStr
.getString());
189 fRes
= (double) ScGlobal::GetCaseCollator()->compareString(
190 rCell1
.maStr
.getString(), rCell2
.maStr
.getString());
193 if (nStringQuery
&& pOptions
)
195 const ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
196 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
199 const ScQueryEntry::Item
& rItem
= rItems
[0];
200 if (rItem
.meType
!= ScQueryEntry::ByString
&& !rItem
.maString
.isEmpty() &&
201 (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
))
203 // As in ScTable::ValidQuery() match a numeric string for a
204 // number query that originated from a string, e.g. in SUMIF
205 // and COUNTIF. Transliteration is not needed here.
207 if (nStringQuery
== 1)
208 bEqual
= rCell1
.maStr
== rItem
.maString
;
210 bEqual
= rCell2
.maStr
== rItem
.maString
;
212 // match => fRes=0, else fRes=1
213 fRes
= double((rEntry
.eOp
== SC_NOT_EQUAL
) ? bEqual
: !bEqual
);
221 double CompareFunc( const Compare::Cell
& rCell1
, double fCell2
, CompareOptions
* pOptions
)
223 // Keep DoubleError if encountered
224 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
225 if (!rCell1
.mbEmpty
&& rCell1
.mbValue
&& !rtl::math::isFinite(rCell1
.mfValue
))
226 return rCell1
.mfValue
;
227 if (!rtl::math::isFinite(fCell2
))
230 bool bStringQuery
= false;
237 fRes
= 1; // empty cell > -x
239 fRes
= -1; // empty cell < x
241 // else: empty cell == 0.0
243 else if (rCell1
.mbValue
)
245 if (!rtl::math::approxEqual(rCell1
.mfValue
, fCell2
))
247 if (rCell1
.mfValue
- fCell2
< 0)
255 fRes
= 1; // string is greater than number
259 if (bStringQuery
&& pOptions
)
261 const ScQueryEntry
& rEntry
= pOptions
->aQueryEntry
;
262 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
265 const ScQueryEntry::Item
& rItem
= rItems
[0];
266 if (rItem
.meType
!= ScQueryEntry::ByString
&& !rItem
.maString
.isEmpty() &&
267 (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
))
269 // As in ScTable::ValidQuery() match a numeric string for a
270 // number query that originated from a string, e.g. in SUMIF
271 // and COUNTIF. Transliteration is not needed here.
272 bool bEqual
= rCell1
.maStr
== rItem
.maString
;
274 // match => fRes=0, else fRes=1
275 fRes
= double((rEntry
.eOp
== SC_NOT_EQUAL
) ? bEqual
: !bEqual
);
283 double CompareFunc( double fCell1
, double fCell2
)
285 // Keep DoubleError if encountered
286 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
287 if (!rtl::math::isFinite(fCell1
))
289 if (!rtl::math::isFinite(fCell2
))
294 if (!rtl::math::approxEqual(fCell1
, fCell2
))
296 if (fCell1
- fCell2
< 0.0)
305 double CompareEmptyToNumericFunc( double fCell2
)
307 // Keep DoubleError if encountered
308 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
309 if (!rtl::math::isFinite(fCell2
))
316 fRes
= 1; // empty cell > -x
318 fRes
= -1; // empty cell < x
320 // else: empty cell == 0.0
327 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */