Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*
3 : : * This file is part of the LibreOffice project.
4 : : *
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/.
8 : : *
9 : : * This file incorporates work covered by the following license notice:
10 : : *
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 .
18 : : */
19 : :
20 : : #include "sal/config.h"
21 : :
22 : : #include <cstddef>
23 : : #include <string.h>
24 : : #include <vector>
25 : : #include <algorithm>
26 : :
27 : : #include <rtl/ustring.hxx>
28 : : #include <rtl/ustrbuf.hxx>
29 : : #include <rtl/string.hxx>
30 : : #include <rtl/strbuf.hxx>
31 : : #include <sal/types.h>
32 : :
33 : : #include <comphelper/string.hxx>
34 : : #include <comphelper/stlunosequence.hxx>
35 : : #include <comphelper/stl_types.hxx>
36 : :
37 : : #include <com/sun/star/i18n/CharType.hpp>
38 : :
39 : :
40 : : namespace comphelper { namespace string {
41 : :
42 : : namespace
43 : : {
44 : 21896 : template <typename T, typename C> T tmpl_stripStart(const T &rIn,
45 : : const C cRemove)
46 : : {
47 [ + + ][ - + ]: 21896 : if (rIn.isEmpty())
48 : 2572 : return rIn;
49 : :
50 : 19324 : sal_Int32 i = 0;
51 : :
52 [ + + ][ + + ]: 21191 : while (i < rIn.getLength())
53 : : {
54 [ + + ][ + + ]: 21160 : if (rIn[i] != cRemove)
55 : 19293 : break;
56 : 1867 : ++i;
57 : : }
58 : :
59 : 21896 : return rIn.copy(i);
60 : : }
61 : : }
62 : :
63 : 2176 : rtl::OString stripStart(const rtl::OString &rIn, sal_Char c)
64 : : {
65 : 2176 : return tmpl_stripStart<rtl::OString, sal_Char>(rIn, c);
66 : : }
67 : :
68 : 19720 : rtl::OUString stripStart(const rtl::OUString &rIn, sal_Unicode c)
69 : : {
70 : 19720 : return tmpl_stripStart<rtl::OUString, sal_Unicode>(rIn, c);
71 : : }
72 : :
73 : : namespace
74 : : {
75 : 30553 : template <typename T, typename C> T tmpl_stripEnd(const T &rIn,
76 : : const C cRemove)
77 : : {
78 [ + + ][ + + ]: 30553 : if (rIn.isEmpty())
79 : 2650 : return rIn;
80 : :
81 : 27903 : sal_Int32 i = rIn.getLength();
82 : :
83 [ + - ][ + + ]: 30295 : while (i > 0)
84 : : {
85 [ + + ][ + + ]: 30292 : if (rIn[i-1] != cRemove)
86 : 27900 : break;
87 : 2392 : --i;
88 : : }
89 : :
90 : 30553 : return rIn.copy(0, i);
91 : : }
92 : : }
93 : :
94 : 520 : rtl::OString stripEnd(const rtl::OString &rIn, sal_Char c)
95 : : {
96 : 520 : return tmpl_stripEnd<rtl::OString, sal_Char>(rIn, c);
97 : : }
98 : :
99 : 30033 : rtl::OUString stripEnd(const rtl::OUString &rIn, sal_Unicode c)
100 : : {
101 : 30033 : return tmpl_stripEnd<rtl::OUString, sal_Unicode>(rIn, c);
102 : : }
103 : :
104 : 508 : rtl::OString strip(const rtl::OString &rIn, sal_Char c)
105 : : {
106 [ + - ]: 508 : return stripEnd(stripStart(rIn, c), c);
107 : : }
108 : :
109 : 17307 : rtl::OUString strip(const rtl::OUString &rIn, sal_Unicode c)
110 : : {
111 [ + - ]: 17307 : return stripEnd(stripStart(rIn, c), c);
112 : : }
113 : :
114 : : namespace
115 : : {
116 : 199838 : template <typename T, typename C> sal_Int32 tmpl_getTokenCount(const T &rIn,
117 : : C cTok)
118 : : {
119 : : // Empty String: TokenCount by Definition is 0
120 [ + + ][ + + ]: 199838 : if (rIn.isEmpty())
121 : 43 : return 0;
122 : :
123 : 199795 : sal_Int32 nTokCount = 1;
124 [ + + ][ + + ]: 3535210 : for (sal_Int32 i = 0; i < rIn.getLength(); ++i)
125 : : {
126 [ + + ][ + + ]: 3335415 : if (rIn[i] == cTok)
127 : 99962 : ++nTokCount;
128 : : }
129 : 199838 : return nTokCount;
130 : : }
131 : : }
132 : :
133 : 197928 : sal_Int32 getTokenCount(const rtl::OString &rIn, sal_Char cTok)
134 : : {
135 : 197928 : return tmpl_getTokenCount<rtl::OString, sal_Char>(rIn, cTok);
136 : : }
137 : :
138 : 1910 : sal_Int32 getTokenCount(const rtl::OUString &rIn, sal_Unicode cTok)
139 : : {
140 : 1910 : return tmpl_getTokenCount<rtl::OUString, sal_Unicode>(rIn, cTok);
141 : : }
142 : :
143 : 52875 : sal_uInt32 decimalStringToNumber(
144 : : ::rtl::OUString const & str )
145 : : {
146 : 52875 : sal_uInt32 result = 0;
147 [ + + ]: 106596 : for( sal_Int32 i = 0 ; i < str.getLength() ; )
148 : : {
149 [ + - ]: 53721 : sal_uInt32 c = str.iterateCodePoints(&i);
150 : 53721 : sal_uInt32 value = 0;
151 [ + + ]: 53721 : if( c <= 0x0039) // ASCII decimal digits, most common
152 : 53712 : value = c - 0x0030;
153 [ + + ]: 9 : else if( c >= 0x1D7F6 ) // mathematical monospace digits
154 : 6 : value = c - 0x1D7F6;
155 [ - + ]: 3 : else if( c >= 0x1D7EC ) // mathematical sans-serif bold digits
156 : 0 : value = c - 0x1D7EC;
157 [ - + ]: 3 : else if( c >= 0x1D7E2 ) // mathematical sans-serif digits
158 : 0 : value = c - 0x1D7E2;
159 [ - + ]: 3 : else if( c >= 0x1D7D8 ) // mathematical double-struck digits
160 : 0 : value = c - 0x1D7D8;
161 [ - + ]: 3 : else if( c >= 0x1D7CE ) // mathematical bold digits
162 : 0 : value = c - 0x1D7CE;
163 [ - + ]: 3 : else if( c >= 0x11066 ) // brahmi digits
164 : 0 : value = c - 0x11066;
165 [ - + ]: 3 : else if( c >= 0x104A0 ) // osmanya digits
166 : 0 : value = c - 0x104A0;
167 [ - + ]: 3 : else if( c >= 0xFF10 ) // fullwidth digits
168 : 0 : value = c - 0xFF10;
169 [ - + ]: 3 : else if( c >= 0xABF0 ) // meetei mayek digits
170 : 0 : value = c - 0xABF0;
171 [ - + ]: 3 : else if( c >= 0xAA50 ) // cham digits
172 : 0 : value = c - 0xAA50;
173 [ - + ]: 3 : else if( c >= 0xA9D0 ) // javanese digits
174 : 0 : value = c - 0xA9D0;
175 [ - + ]: 3 : else if( c >= 0xA900 ) // kayah li digits
176 : 0 : value = c - 0xA900;
177 [ - + ]: 3 : else if( c >= 0xA8D0 ) // saurashtra digits
178 : 0 : value = c - 0xA8D0;
179 [ - + ]: 3 : else if( c >= 0xA620 ) // vai digits
180 : 0 : value = c - 0xA620;
181 [ - + ]: 3 : else if( c >= 0x1C50 ) // ol chiki digits
182 : 0 : value = c - 0x1C50;
183 [ - + ]: 3 : else if( c >= 0x1C40 ) // lepcha digits
184 : 0 : value = c - 0x1C40;
185 [ - + ]: 3 : else if( c >= 0x1BB0 ) // sundanese digits
186 : 0 : value = c - 0x1BB0;
187 [ - + ]: 3 : else if( c >= 0x1B50 ) // balinese digits
188 : 0 : value = c - 0x1B50;
189 [ - + ]: 3 : else if( c >= 0x1A90 ) // tai tham tham digits
190 : 0 : value = c - 0x1A90;
191 [ - + ]: 3 : else if( c >= 0x1A80 ) // tai tham hora digits
192 : 0 : value = c - 0x1A80;
193 [ - + ]: 3 : else if( c >= 0x19D0 ) // new tai lue digits
194 : 0 : value = c - 0x19D0;
195 [ - + ]: 3 : else if( c >= 0x1946 ) // limbu digits
196 : 0 : value = c - 0x1946;
197 [ - + ]: 3 : else if( c >= 0x1810 ) // mongolian digits
198 : 0 : value = c - 0x1810;
199 [ - + ]: 3 : else if( c >= 0x17E0 ) // khmer digits
200 : 0 : value = c - 0x17E0;
201 [ - + ]: 3 : else if( c >= 0x1090 ) // myanmar shan digits
202 : 0 : value = c - 0x1090;
203 [ - + ]: 3 : else if( c >= 0x1040 ) // myanmar digits
204 : 0 : value = c - 0x1040;
205 [ - + ]: 3 : else if( c >= 0x0F20 ) // tibetan digits
206 : 0 : value = c - 0x0F20;
207 [ - + ]: 3 : else if( c >= 0x0ED0 ) // lao digits
208 : 0 : value = c - 0x0ED0;
209 [ - + ]: 3 : else if( c >= 0x0E50 ) // thai digits
210 : 0 : value = c - 0x0E50;
211 [ - + ]: 3 : else if( c >= 0x0D66 ) // malayalam digits
212 : 0 : value = c - 0x0D66;
213 [ - + ]: 3 : else if( c >= 0x0CE6 ) // kannada digits
214 : 0 : value = c - 0x0CE6;
215 [ - + ]: 3 : else if( c >= 0x0C66 ) // telugu digits
216 : 0 : value = c - 0x0C66;
217 [ - + ]: 3 : else if( c >= 0x0BE6 ) // tamil digits
218 : 0 : value = c - 0x0BE6;
219 [ - + ]: 3 : else if( c >= 0x0B66 ) // oriya digits
220 : 0 : value = c - 0x0B66;
221 [ - + ]: 3 : else if( c >= 0x0AE6 ) // gujarati digits
222 : 0 : value = c - 0x0AE6;
223 [ - + ]: 3 : else if( c >= 0x0A66 ) // gurmukhi digits
224 : 0 : value = c - 0x0A66;
225 [ - + ]: 3 : else if( c >= 0x09E6 ) // bengali digits
226 : 0 : value = c - 0x09E6;
227 [ - + ]: 3 : else if( c >= 0x0966 ) // devanagari digit
228 : 0 : value = c - 0x0966;
229 [ + - ]: 3 : else if( c >= 0x07C0 ) // nko digits
230 : 3 : value = c - 0x07C0;
231 [ # # ]: 0 : else if( c >= 0x06F0 ) // extended arabic-indic digits
232 : 0 : value = c - 0x06F0;
233 [ # # ]: 0 : else if( c >= 0x0660 ) // arabic-indic digits
234 : 0 : value = c - 0x0660;
235 : 53721 : result = result * 10 + value;
236 : : }
237 : 52875 : return result;
238 : : }
239 : :
240 : : using namespace ::com::sun::star;
241 : :
242 : : // convert between sequence of string and comma separated string
243 : :
244 : 89 : ::rtl::OUString convertCommaSeparated(
245 : : uno::Sequence< ::rtl::OUString > const& i_rSeq)
246 : : {
247 : 89 : ::rtl::OUStringBuffer buf;
248 : : ::comphelper::intersperse(
249 : : ::comphelper::stl_begin(i_rSeq), ::comphelper::stl_end(i_rSeq),
250 : : ::comphelper::OUStringBufferAppender(buf),
251 [ + - ][ + - ]: 89 : ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ", " )));
[ + - ][ + - ]
252 [ + - ]: 89 : return buf.makeStringAndClear();
253 : : }
254 : :
255 : : uno::Sequence< ::rtl::OUString >
256 : 72 : convertCommaSeparated( ::rtl::OUString const& i_rString )
257 : : {
258 [ + - ]: 72 : std::vector< ::rtl::OUString > vec;
259 : 72 : sal_Int32 idx = 0;
260 [ + + ]: 78 : do {
261 : : ::rtl::OUString kw =
262 : 78 : i_rString.getToken(0, static_cast<sal_Unicode> (','), idx);
263 : 78 : kw = kw.trim();
264 [ + + ]: 78 : if (!kw.isEmpty()) {
265 [ + - ]: 12 : vec.push_back(kw);
266 : 78 : }
267 : : } while (idx >= 0);
268 [ + - ]: 72 : uno::Sequence< ::rtl::OUString > kws(vec.size());
269 [ + - ][ + - ]: 72 : std::copy(vec.begin(), vec.end(), stl_begin(kws));
270 : 72 : return kws;
271 : : }
272 : :
273 : :
274 : 82470 : sal_Int32 compareNatural( const ::rtl::OUString & rLHS, const ::rtl::OUString & rRHS,
275 : : const uno::Reference< i18n::XCollator > &rCollator,
276 : : const uno::Reference< i18n::XBreakIterator > &rBI,
277 : : const lang::Locale &rLocale )
278 : : {
279 : 82470 : sal_Int32 nRet = 0;
280 : :
281 : 82470 : sal_Int32 nLHSLastNonDigitPos = 0;
282 : 82470 : sal_Int32 nRHSLastNonDigitPos = 0;
283 : 82470 : sal_Int32 nLHSFirstDigitPos = 0;
284 : 82470 : sal_Int32 nRHSFirstDigitPos = 0;
285 : :
286 [ + + ][ - + ]: 82479 : while (nLHSFirstDigitPos < rLHS.getLength() || nRHSFirstDigitPos < rRHS.getLength())
[ + + ]
287 : : {
288 : : sal_Int32 nLHSChunkLen;
289 : : sal_Int32 nRHSChunkLen;
290 : :
291 : : //Compare non digit block as normal strings
292 : 82473 : nLHSFirstDigitPos = rBI->nextCharBlock(rLHS, nLHSLastNonDigitPos,
293 : 82473 : rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
294 : 82473 : nRHSFirstDigitPos = rBI->nextCharBlock(rRHS, nRHSLastNonDigitPos,
295 : 82473 : rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
296 [ + + ]: 82473 : if (nLHSFirstDigitPos == -1)
297 : 42811 : nLHSFirstDigitPos = rLHS.getLength();
298 [ + + ]: 82473 : if (nRHSFirstDigitPos == -1)
299 : 26363 : nRHSFirstDigitPos = rRHS.getLength();
300 : 82473 : nLHSChunkLen = nLHSFirstDigitPos - nLHSLastNonDigitPos;
301 : 82473 : nRHSChunkLen = nRHSFirstDigitPos - nRHSLastNonDigitPos;
302 : :
303 : 82473 : nRet = rCollator->compareSubstring(rLHS, nLHSLastNonDigitPos,
304 : 82473 : nLHSChunkLen, rRHS, nRHSLastNonDigitPos, nRHSChunkLen);
305 [ + + ]: 82473 : if (nRet != 0)
306 : 56040 : break;
307 : :
308 : : //Compare digit block as one number vs another
309 : 26433 : nLHSLastNonDigitPos = rBI->endOfCharBlock(rLHS, nLHSFirstDigitPos,
310 : 26433 : rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
311 : 26433 : nRHSLastNonDigitPos = rBI->endOfCharBlock(rRHS, nRHSFirstDigitPos,
312 : 26433 : rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
313 [ + + ]: 26433 : if (nLHSLastNonDigitPos == -1)
314 : 38 : nLHSLastNonDigitPos = rLHS.getLength();
315 [ + + ]: 26433 : if (nRHSLastNonDigitPos == -1)
316 : 26 : nRHSLastNonDigitPos = rRHS.getLength();
317 : 26433 : nLHSChunkLen = nLHSLastNonDigitPos - nLHSFirstDigitPos;
318 : 26433 : nRHSChunkLen = nRHSLastNonDigitPos - nRHSFirstDigitPos;
319 : :
320 : : //To-Do: Possibly scale down those unicode codepoints that relate to
321 : : //numbers outside of the normal 0-9 range, e.g. see GetLocalizedChar in
322 : : //vcl
323 : :
324 [ + - ]: 26433 : sal_uInt32 nLHS = comphelper::string::decimalStringToNumber(rLHS.copy(nLHSFirstDigitPos, nLHSChunkLen));
325 [ + - ]: 26433 : sal_uInt32 nRHS = comphelper::string::decimalStringToNumber(rRHS.copy(nRHSFirstDigitPos, nRHSChunkLen));
326 : :
327 : 26433 : nRet = nLHS-nRHS;
328 [ + + ]: 26433 : if (nRet != 0)
329 : 26424 : break;
330 : : }
331 : :
332 : : //Squeeze these down to -1, 0, 1 in case it gets casted to a StringCompare
333 [ + + ]: 82470 : if (nRet > 0)
334 : 73529 : nRet = 1;
335 [ + + ]: 8941 : else if (nRet < 0)
336 : 8935 : nRet = -1;
337 : :
338 : 82470 : return nRet;
339 : : }
340 : :
341 : 522 : NaturalStringSorter::NaturalStringSorter(
342 : : const uno::Reference< uno::XComponentContext > &rContext,
343 : 522 : const lang::Locale &rLocale) : m_aLocale(rLocale)
344 : : {
345 [ + - ]: 522 : uno::Reference< lang::XMultiComponentFactory > xFactory(rContext->getServiceManager(),
346 [ + - ][ + - ]: 522 : uno::UNO_SET_THROW);
347 : :
348 [ + - ]: 522 : m_xCollator = uno::Reference< i18n::XCollator >(xFactory->createInstanceWithContext(
349 : 522 : rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.i18n.Collator")), rContext),
350 [ + - ][ + - ]: 522 : uno::UNO_QUERY_THROW);
[ + - ][ + - ]
351 [ + - ][ + - ]: 522 : m_xCollator->loadDefaultCollator(m_aLocale, 0);
352 [ + - ]: 522 : m_xBI = uno::Reference< i18n::XBreakIterator >(xFactory->createInstanceWithContext(
353 : 522 : rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.i18n.BreakIterator")), rContext),
354 [ + - ][ + - ]: 522 : uno::UNO_QUERY_THROW);
[ + - ][ + - ]
355 : 522 : }
356 : :
357 : : namespace
358 : : {
359 : : //do OPER on each element of the string, return false
360 : : //if any OPER is false, true otherwise
361 : : template <bool (*OPER)(sal_Unicode), typename T>
362 : 837 : bool tmpl_is_OPER_AsciiString(const T &rString)
363 : : {
364 [ # # ][ + + ]: 3485 : for (sal_Int32 i = 0; i < rString.getLength(); ++i)
365 : : {
366 [ # # ][ + + ]: 2775 : if (!OPER(rString[i]))
367 : 127 : return false;
368 : : }
369 : 837 : return true;
370 : : }
371 : : }
372 : :
373 : 837 : bool isdigitAsciiString(const rtl::OString &rString)
374 : : {
375 : 837 : return tmpl_is_OPER_AsciiString<isdigitAscii>(rString);
376 : : }
377 : :
378 : 0 : bool isdigitAsciiString(const rtl::OUString &rString)
379 : : {
380 : 0 : return tmpl_is_OPER_AsciiString<isdigitAscii>(rString);
381 : : }
382 : :
383 : : namespace
384 : : {
385 : 294381 : template <typename T, typename U> T* string_alloc(sal_Int32 nLen)
386 : : {
387 : : //Clearly this is somewhat cosy with the sal implmentation
388 : :
389 : : //rtl_[u]String contains U buffer[1], so an input of nLen
390 : : //allocates a buffer of nLen + 1 and we'll ensure a null termination
391 : :
392 : : T* newStr =
393 : : (sal::static_int_cast< sal_uInt32 >(nLen)
394 : : <= ((SAL_MAX_UINT32 - sizeof (T))
395 : : / sizeof (U)))
396 : : ? (T*) rtl_allocateMemory(
397 : : sizeof (T) + nLen * sizeof (U))
398 [ + - ][ + - ]: 294381 : : NULL;
399 : :
400 [ - + ][ - + ]: 294381 : if (!newStr)
401 : 0 : throw std::bad_alloc();
402 : :
403 : 294381 : newStr->refCount = 1;
404 : 294381 : newStr->length = nLen;
405 : 294381 : newStr->buffer[nLen] = 0;
406 : 294381 : return newStr;
407 : : }
408 : : }
409 : :
410 : 123093 : rtl_uString * SAL_CALL rtl_uString_alloc(sal_Int32 nLen)
411 : : {
412 : 123093 : return string_alloc<rtl_uString, sal_Unicode>(nLen);
413 : : }
414 : :
415 : 171288 : rtl_String * SAL_CALL rtl_string_alloc(sal_Int32 nLen)
416 : : {
417 : 171288 : return string_alloc<rtl_String, sal_Char>(nLen);
418 : : }
419 : :
420 : : namespace
421 : : {
422 : 402 : template <typename T, typename O> T tmpl_reverseString(const T &rIn)
423 : : {
424 [ - + ][ - + ]: 402 : if (rIn.isEmpty())
425 : 0 : return rIn;
426 : :
427 : 402 : sal_Int32 i = rIn.getLength();
428 : 402 : O sBuf(i);
429 [ + + ][ + + ]: 1582 : while (i)
430 [ + - ][ + - ]: 1180 : sBuf.append(rIn[--i]);
431 [ + - ]: 402 : return sBuf.makeStringAndClear();
432 : : }
433 : : }
434 : :
435 : 399 : rtl::OUString reverseString(const rtl::OUString &rStr)
436 : : {
437 : 399 : return tmpl_reverseString<rtl::OUString, rtl::OUStringBuffer>(rStr);
438 : : }
439 : :
440 : 3 : rtl::OString reverseString(const rtl::OString &rStr)
441 : : {
442 : 3 : return tmpl_reverseString<rtl::OString, rtl::OStringBuffer>(rStr);
443 : : }
444 : :
445 : :
446 : : } }
447 : :
448 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|