Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / tool / compare.cxx
blob36e18bc5279bd55dfd27fe44b187de526658a732
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
27 namespace sc {
29 Compare::Cell::Cell() :
30 mfValue(0.0), mbValue(false), mbEmpty(false) {}
32 Compare::Compare() :
33 meOp(SC_EQUAL), mbIgnoreCase(true) {}
35 CompareOptions::CompareOptions( ScDocument* pDoc, const ScQueryEntry& rEntry, bool bReg ) :
36 aQueryEntry(rEntry),
37 bRegEx(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
43 // struct if needed.
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
56 double fRes = 0;
57 if (rCell1.mbEmpty)
59 if (rCell2.mbEmpty)
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
67 else
68 fRes = -1; // empty cell < x
70 // else: empty cell == 0.0
72 else
74 if (!rCell2.maStr.isEmpty())
75 fRes = -1; // empty cell < "..."
76 // else: empty cell == ""
79 else if (rCell2.mbEmpty)
81 if (rCell1.mbValue)
83 if (rCell1.mfValue != 0.0)
85 if (rCell1.mfValue < 0.0)
86 fRes = -1; // -x < empty cell
87 else
88 fRes = 1; // x > empty cell
90 // else: empty cell == 0.0
92 else
94 if (!rCell1.maStr.isEmpty())
95 fRes = 1; // "..." > empty cell
96 // else: "" == empty cell
99 else if (rCell1.mbValue)
101 if (rCell2.mbValue)
103 if (!rtl::math::approxEqual(rCell1.mfValue, rCell2.mfValue))
105 if (rCell1.mfValue - rCell2.mfValue < 0)
106 fRes = -1;
107 else
108 fRes = 1;
111 else
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
122 else
124 // Both strings.
125 if (pOptions)
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());
148 bool bMatch = false;
149 if (pOptions->bMatchWholeCell)
151 if (bIgnoreCase)
152 bMatch = rCell1.maStr.getDataIgnoreCase() == rCell2.maStr.getDataIgnoreCase();
153 else
154 bMatch = rCell1.maStr.getData() == rCell2.maStr.getData();
156 else
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());
171 else
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());
178 else
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();
187 if (!rItems.empty())
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.
196 bool bEqual = false;
197 if (nStringQuery == 1)
198 bEqual = rCell1.maStr == rItem.maString;
199 else
200 bEqual = rCell2.maStr == rItem.maString;
202 // match => fRes=0, else fRes=1
203 fRes = (rEntry.eOp == SC_NOT_EQUAL) ? bEqual : !bEqual;
208 return fRes;
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))
216 return fCell1;
217 if (!rCell2.mbEmpty && rCell2.mbValue && !rtl::math::isFinite(rCell2.mfValue))
218 return rCell2.mfValue;
220 bool bStringQuery = false;
221 double fRes = 0;
222 if (rCell2.mbEmpty)
224 if (fCell1 != 0.0)
226 if (fCell1 < 0.0)
227 fRes = -1; // -x < empty cell
228 else
229 fRes = 1; // x > empty cell
231 // else: empty cell == 0.0
233 else
235 if (rCell2.mbValue)
237 if (!rtl::math::approxEqual(fCell1, rCell2.mfValue))
239 if (fCell1 - rCell2.mfValue < 0)
240 fRes = -1;
241 else
242 fRes = 1;
245 else
247 fRes = -1; // number is less than string
248 bStringQuery = true;
252 if (bStringQuery && pOptions)
254 const ScQueryEntry& rEntry = pOptions->aQueryEntry;
255 const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
256 if (!rItems.empty())
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;
273 return fRes;
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))
283 return fCell2;
285 bool bStringQuery = false;
286 double fRes = 0;
287 if (rCell1.mbEmpty)
289 if (fCell2 != 0.0)
291 if (fCell2 < 0.0)
292 fRes = 1; // empty cell > -x
293 else
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)
303 fRes = -1;
304 else
305 fRes = 1;
308 else
310 fRes = 1; // string is greater than number
311 bStringQuery = true;
314 if (bStringQuery && pOptions)
316 const ScQueryEntry& rEntry = pOptions->aQueryEntry;
317 const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
318 if (!rItems.empty())
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;
335 return fRes;
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))
343 return fCell1;
344 if (!rtl::math::isFinite(fCell2))
345 return fCell2;
347 double fRes = 0.0;
349 if (!rtl::math::approxEqual(fCell1, fCell2))
351 if (fCell1 - fCell2 < 0.0)
352 fRes = -1;
353 else
354 fRes = 1;
357 return fRes;
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))
365 return fCell2;
367 double fRes = 0;
368 if (fCell2 != 0.0)
370 if (fCell2 < 0.0)
371 fRes = 1; // empty cell > -x
372 else
373 fRes = -1; // empty cell < x
375 // else: empty cell == 0.0
377 return fRes;
382 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */