Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/comphelper/source/misc/anycompare.cxx
Line
Count
Source
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 <memory>
21
#include <optional>
22
#include <comphelper/anycompare.hxx>
23
#include <typelib/typedescription.hxx>
24
25
#include <com/sun/star/util/Date.hpp>
26
#include <com/sun/star/util/Time.hpp>
27
#include <com/sun/star/util/DateTime.hpp>
28
29
#include "typedescriptionref.hxx"
30
31
namespace comphelper
32
{
33
    using ::com::sun::star::uno::Reference;
34
    using ::com::sun::star::uno::Type;
35
    using ::com::sun::star::uno::TypeDescription;
36
    using ::com::sun::star::uno::TypeClass_CHAR;
37
    using ::com::sun::star::uno::TypeClass_BOOLEAN;
38
    using ::com::sun::star::uno::TypeClass_BYTE;
39
    using ::com::sun::star::uno::TypeClass_SHORT;
40
    using ::com::sun::star::uno::TypeClass_UNSIGNED_SHORT;
41
    using ::com::sun::star::uno::TypeClass_LONG;
42
    using ::com::sun::star::uno::TypeClass_UNSIGNED_LONG;
43
    using ::com::sun::star::uno::TypeClass_HYPER;
44
    using ::com::sun::star::uno::TypeClass_UNSIGNED_HYPER;
45
    using ::com::sun::star::uno::TypeClass_FLOAT;
46
    using ::com::sun::star::uno::TypeClass_DOUBLE;
47
    using ::com::sun::star::uno::TypeClass_STRING;
48
    using ::com::sun::star::uno::TypeClass_TYPE;
49
    using ::com::sun::star::uno::TypeClass_ENUM;
50
    using ::com::sun::star::uno::TypeClass_INTERFACE;
51
    using ::com::sun::star::uno::TypeClass_STRUCT;
52
    using ::com::sun::star::i18n::XCollator;
53
    using ::com::sun::star::util::Date;
54
    using ::com::sun::star::util::Time;
55
    using ::com::sun::star::util::DateTime;
56
    using ::comphelper::detail::TypeDescriptionRef;
57
58
    namespace {
59
60
    class DatePredicateLess : public IKeyPredicateLess
61
    {
62
    public:
63
        virtual bool isLess( css::uno::Any const & _lhs, css::uno::Any const & _rhs ) const override
64
0
        {
65
0
            Date lhs, rhs;
66
0
            if  (   !( _lhs >>= lhs )
67
0
                ||  !( _rhs >>= rhs )
68
0
                )
69
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
70
            // FIXME Timezone?
71
72
0
            if ( lhs.Year < rhs.Year )
73
0
                return true;
74
0
            if ( lhs.Year > rhs.Year )
75
0
                return false;
76
77
0
            if ( lhs.Month < rhs.Month )
78
0
                return true;
79
0
            if ( lhs.Month > rhs.Month )
80
0
                return false;
81
82
0
            if ( lhs.Day < rhs.Day )
83
0
                return true;
84
0
            return false;
85
0
        }
86
    };
87
88
    class TimePredicateLess : public IKeyPredicateLess
89
    {
90
    public:
91
        virtual bool isLess( css::uno::Any const & _lhs, css::uno::Any const & _rhs ) const override
92
0
        {
93
0
            Time lhs, rhs;
94
0
            if  (   !( _lhs >>= lhs )
95
0
                ||  !( _rhs >>= rhs )
96
0
                )
97
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
98
            // FIXME Timezone?
99
100
0
            if ( lhs.Hours < rhs.Hours )
101
0
                return true;
102
0
            if ( lhs.Hours > rhs.Hours )
103
0
                return false;
104
105
0
            if ( lhs.Minutes < rhs.Minutes )
106
0
                return true;
107
0
            if ( lhs.Minutes > rhs.Minutes )
108
0
                return false;
109
110
0
            if ( lhs.Seconds < rhs.Seconds )
111
0
                return true;
112
0
            if ( lhs.Seconds > rhs.Seconds )
113
0
                return false;
114
115
0
            if ( lhs.NanoSeconds < rhs.NanoSeconds )
116
0
                return true;
117
0
            return false;
118
0
        }
119
    };
120
121
    class DateTimePredicateLess : public IKeyPredicateLess
122
    {
123
    public:
124
        virtual bool isLess( css::uno::Any const & _lhs, css::uno::Any const & _rhs ) const override
125
0
        {
126
0
            DateTime lhs, rhs;
127
0
            if  (   !( _lhs >>= lhs )
128
0
                ||  !( _rhs >>= rhs )
129
0
                )
130
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
131
            // FIXME Timezone?
132
133
0
            if ( lhs.Year < rhs.Year )
134
0
                return true;
135
0
            if ( lhs.Year > rhs.Year )
136
0
                return false;
137
138
0
            if ( lhs.Month < rhs.Month )
139
0
                return true;
140
0
            if ( lhs.Month > rhs.Month )
141
0
                return false;
142
143
0
            if ( lhs.Day < rhs.Day )
144
0
                return true;
145
0
            if ( lhs.Day > rhs.Day )
146
0
                return false;
147
148
0
            if ( lhs.Hours < rhs.Hours )
149
0
                return true;
150
0
            if ( lhs.Hours > rhs.Hours )
151
0
                return false;
152
153
0
            if ( lhs.Minutes < rhs.Minutes )
154
0
                return true;
155
0
            if ( lhs.Minutes > rhs.Minutes )
156
0
                return false;
157
158
0
            if ( lhs.Seconds < rhs.Seconds )
159
0
                return true;
160
0
            if ( lhs.Seconds > rhs.Seconds )
161
0
                return false;
162
163
0
            if ( lhs.NanoSeconds < rhs.NanoSeconds )
164
0
                return true;
165
0
            return false;
166
0
        }
167
    };
168
169
    bool anyLess( void const * lhs, typelib_TypeDescriptionReference * lhsType,
170
                  void const * rhs, typelib_TypeDescriptionReference * rhsType );
171
172
    // For compound types we need to compare them member by member until we've
173
    // checked them all or found a member that differs. For inequality checks
174
    // we need to call anyLess() twice in both directions, this function does that.
175
    std::optional<bool> anyCompare( void const * lhs, typelib_TypeDescriptionReference * lhsType,
176
              void const * rhs, typelib_TypeDescriptionReference * rhsType )
177
0
    {
178
0
        if( anyLess( lhs, lhsType, rhs, rhsType ))
179
0
            return std::optional( true );
180
0
        if( anyLess( rhs, rhsType, lhs, lhsType ))
181
0
            return std::optional( false );
182
0
        return std::nullopt; // equal, so can't yet tell if anyLess() should return
183
0
    }
184
185
    // This is typelib_typedescription_equals(), but returns -1/0/1 values like strcmp().
186
    int compareTypes( const typelib_TypeDescription * lhsType,
187
                      const typelib_TypeDescription * rhsType )
188
1.20k
    {
189
1.20k
        if( lhsType == rhsType )
190
1.20k
            return 0;
191
0
        if( lhsType->eTypeClass != rhsType->eTypeClass )
192
0
            return lhsType->eTypeClass - rhsType->eTypeClass;
193
0
        if( lhsType->pTypeName->length != rhsType->pTypeName->length )
194
0
            return lhsType->pTypeName->length - rhsType->pTypeName->length;
195
0
        return rtl_ustr_compare( lhsType->pTypeName->buffer, rhsType->pTypeName->buffer );
196
0
    }
197
198
    bool anyLess( void const * lhs, typelib_TypeDescriptionReference * lhsType,
199
                  void const * rhs, typelib_TypeDescriptionReference * rhsType )
200
270k
    {
201
270k
        if (lhsType->eTypeClass != rhsType->eTypeClass)
202
0
            return lhsType->eTypeClass < rhsType->eTypeClass;
203
204
270k
        if (lhsType->eTypeClass == typelib_TypeClass_VOID) {
205
0
            return false;
206
0
        }
207
270k
        assert(lhs != nullptr);
208
270k
        assert(rhs != nullptr);
209
210
270k
        switch (lhsType->eTypeClass) {
211
0
        case typelib_TypeClass_INTERFACE:
212
0
            return lhs < rhs;
213
0
        case typelib_TypeClass_STRUCT:
214
0
        case typelib_TypeClass_EXCEPTION: {
215
0
            TypeDescription lhsTypeDescr( lhsType );
216
0
            if (!lhsTypeDescr.is())
217
0
                lhsTypeDescr.makeComplete();
218
0
            if (!lhsTypeDescr.is())
219
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
220
0
            TypeDescription rhsTypeDescr( rhsType );
221
0
            if (!rhsTypeDescr.is())
222
0
                rhsTypeDescr.makeComplete();
223
0
            if (!rhsTypeDescr.is())
224
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
225
0
            int compare = compareTypes( lhsTypeDescr.get(), rhsTypeDescr.get());
226
0
            if( compare != 0 )
227
0
                return compare < 0;
228
229
0
            typelib_CompoundTypeDescription * compType =
230
0
                reinterpret_cast< typelib_CompoundTypeDescription * >(
231
0
                    lhsTypeDescr.get() );
232
0
            sal_Int32 nDescr = compType->nMembers;
233
234
0
            if (compType->pBaseTypeDescription) {
235
0
                std::optional<bool> subLess = anyCompare(
236
0
                    lhs, reinterpret_cast<
237
0
                    typelib_TypeDescription * >(
238
0
                        compType->pBaseTypeDescription)->pWeakRef,
239
0
                    rhs, reinterpret_cast<
240
0
                    typelib_TypeDescription * >(
241
0
                        compType->pBaseTypeDescription)->pWeakRef);
242
0
                if(subLess.has_value())
243
0
                    return *subLess;
244
0
            }
245
246
0
            typelib_TypeDescriptionReference ** ppTypeRefs =
247
0
                compType->ppTypeRefs;
248
0
            sal_Int32 * memberOffsets = compType->pMemberOffsets;
249
250
0
            for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos )
251
0
            {
252
0
                TypeDescriptionRef memberType( ppTypeRefs[ nPos ] );
253
0
                if (!memberType.is())
254
0
                    throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
255
0
                std::optional<bool> subLess = anyCompare(
256
0
                     static_cast< char const * >(
257
0
                         lhs ) + memberOffsets[ nPos ],
258
0
                     memberType->pWeakRef,
259
0
                     static_cast< char const * >(
260
0
                         rhs ) + memberOffsets[ nPos ],
261
0
                     memberType->pWeakRef);
262
0
                if(subLess.has_value())
263
0
                    return *subLess;
264
0
            }
265
0
            return false; // equal
266
0
        }
267
0
        case typelib_TypeClass_SEQUENCE: {
268
0
            uno_Sequence * lhsSeq = *static_cast< uno_Sequence * const * >(lhs);
269
0
            uno_Sequence * rhsSeq = *static_cast< uno_Sequence * const * >(rhs);
270
0
            if( lhsSeq->nElements != rhsSeq->nElements)
271
0
                return lhsSeq->nElements < rhsSeq->nElements;
272
0
            sal_Int32 nElements = lhsSeq->nElements;
273
274
0
            TypeDescriptionRef lhsTypeDescr( lhsType );
275
0
            if (!lhsTypeDescr.is())
276
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
277
0
            TypeDescriptionRef rhsTypeDescr( rhsType );
278
0
            if (!rhsTypeDescr.is())
279
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
280
0
            int compare = compareTypes( lhsTypeDescr.get(), rhsTypeDescr.get());
281
0
            if( compare != 0 )
282
0
                return compare < 0;
283
284
0
            typelib_TypeDescriptionReference * elementTypeRef =
285
0
                reinterpret_cast< typelib_IndirectTypeDescription * >(lhsTypeDescr.get())->pType;
286
0
            TypeDescriptionRef elementTypeDescr( elementTypeRef );
287
0
            if (!elementTypeDescr.is())
288
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
289
0
            assert( elementTypeDescr.equals( TypeDescriptionRef(
290
0
                        reinterpret_cast< typelib_IndirectTypeDescription * >(lhsTypeDescr.get())->pType )));
291
292
0
            sal_Int32 nElementSize = elementTypeDescr->nSize;
293
0
            if (nElements > 0)
294
0
            {
295
0
                char const * lhsElements = lhsSeq->elements;
296
0
                char const * rhsElements = rhsSeq->elements;
297
0
                for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos )
298
0
                {
299
0
                    std::optional<bool> subLess = anyCompare(
300
0
                        lhsElements + (nElementSize * nPos),
301
0
                        elementTypeDescr->pWeakRef,
302
0
                        rhsElements + (nElementSize * nPos),
303
0
                        elementTypeDescr->pWeakRef );
304
0
                    if(subLess.has_value())
305
0
                        return *subLess;
306
0
                }
307
0
            }
308
0
            return false; // equal
309
0
        }
310
0
        case typelib_TypeClass_ANY: {
311
0
            uno_Any const * lhsAny = static_cast< uno_Any const * >(lhs);
312
0
            uno_Any const * rhsAny = static_cast< uno_Any const * >(rhs);
313
0
            return anyLess( lhsAny->pData, lhsAny->pType, rhsAny->pData, rhsAny->pType );
314
0
        }
315
0
        case typelib_TypeClass_TYPE: {
316
0
            OUString const & lhsTypeName = OUString::unacquired(
317
0
                &(*static_cast< typelib_TypeDescriptionReference * const * >(lhs))->pTypeName);
318
0
            OUString const & rhsTypeName = OUString::unacquired(
319
0
                &(*static_cast< typelib_TypeDescriptionReference * const * >(rhs))->pTypeName);
320
0
            return lhsTypeName < rhsTypeName;
321
0
        }
322
53.7k
        case typelib_TypeClass_STRING: {
323
53.7k
            OUString const & lhsStr = OUString::unacquired(
324
53.7k
                static_cast< rtl_uString * const * >(lhs) );
325
53.7k
            OUString const & rhsStr = OUString::unacquired(
326
53.7k
                static_cast< rtl_uString * const * >(rhs) );
327
53.7k
            return lhsStr < rhsStr;
328
0
        }
329
1.20k
        case typelib_TypeClass_ENUM: {
330
1.20k
            TypeDescription lhsTypeDescr( lhsType );
331
1.20k
            if (!lhsTypeDescr.is())
332
0
                lhsTypeDescr.makeComplete();
333
1.20k
            if (!lhsTypeDescr.is())
334
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
335
1.20k
            TypeDescription rhsTypeDescr( rhsType );
336
1.20k
            if (!rhsTypeDescr.is())
337
0
                rhsTypeDescr.makeComplete();
338
1.20k
            if (!rhsTypeDescr.is())
339
0
                throw css::lang::IllegalArgumentException(u"bad ordering"_ustr, css::uno::Reference<css::uno::XInterface>(), -1);
340
1.20k
            int compare = compareTypes( lhsTypeDescr.get(), rhsTypeDescr.get());
341
1.20k
            if( compare != 0 )
342
0
                return compare < 0;
343
344
1.20k
            return *static_cast< int const * >(lhs) < *static_cast< int const * >(rhs);
345
1.20k
        }
346
49.9k
        case typelib_TypeClass_BOOLEAN:
347
49.9k
            return *static_cast< sal_Bool const * >(lhs) < *static_cast< sal_Bool const * >(rhs);
348
0
        case typelib_TypeClass_CHAR:
349
0
            return *static_cast< sal_Unicode const * >(lhs) < *static_cast< sal_Unicode const * >(rhs);
350
0
        case typelib_TypeClass_FLOAT:
351
0
            return *static_cast< float const * >(lhs) < *static_cast< float const * >(rhs);
352
1.20k
        case typelib_TypeClass_DOUBLE:
353
1.20k
            return *static_cast< double const * >(lhs) < *static_cast< double const * >(rhs);
354
0
        case typelib_TypeClass_BYTE:
355
0
            return *static_cast< sal_Int8 const * >(lhs) < *static_cast< sal_Int8 const * >(rhs);
356
78.6k
        case typelib_TypeClass_SHORT:
357
78.6k
            return *static_cast< sal_Int16 const * >(lhs) < *static_cast< sal_Int16 const * >(rhs);
358
1.20k
        case typelib_TypeClass_UNSIGNED_SHORT:
359
1.20k
            return *static_cast< sal_uInt16 const * >(lhs) < *static_cast< sal_uInt16 const * >(rhs);
360
84.8k
        case typelib_TypeClass_LONG:
361
84.8k
            return *static_cast< sal_Int32 const * >(lhs) < *static_cast< sal_Int32 const * >(rhs);
362
0
        case typelib_TypeClass_UNSIGNED_LONG:
363
0
            return *static_cast< sal_uInt32 const * >(lhs) < *static_cast< sal_uInt32 const * >(rhs);
364
0
        case typelib_TypeClass_HYPER:
365
0
            return *static_cast< sal_Int64 const * >(lhs) < *static_cast< sal_Int64 const * >(rhs);
366
0
        case typelib_TypeClass_UNSIGNED_HYPER:
367
0
            return *static_cast< sal_uInt64 const * >(lhs) < *static_cast< sal_uInt64 const * >(rhs);
368
    //     case typelib_TypeClass_UNKNOWN:
369
    //     case typelib_TypeClass_SERVICE:
370
    //     case typelib_TypeClass_MODULE:
371
0
        default:
372
0
            return false;
373
270k
        }
374
270k
    }
375
376
    } // namespace
377
378
    std::unique_ptr< IKeyPredicateLess > getStandardLessPredicate( Type const & i_type, Reference< XCollator > const & i_collator )
379
0
    {
380
0
        std::unique_ptr< IKeyPredicateLess > pComparator;
381
0
        switch ( i_type.getTypeClass() )
382
0
        {
383
0
        case TypeClass_CHAR:
384
0
            pComparator.reset( new ScalarPredicateLess< sal_Unicode > );
385
0
            break;
386
0
        case TypeClass_BOOLEAN:
387
0
            pComparator.reset( new ScalarPredicateLess< bool > );
388
0
            break;
389
0
        case TypeClass_BYTE:
390
0
            pComparator.reset( new ScalarPredicateLess< sal_Int8 > );
391
0
            break;
392
0
        case TypeClass_SHORT:
393
0
            pComparator.reset( new ScalarPredicateLess< sal_Int16 > );
394
0
            break;
395
0
        case TypeClass_UNSIGNED_SHORT:
396
0
            pComparator.reset( new ScalarPredicateLess< sal_uInt16 > );
397
0
            break;
398
0
        case TypeClass_LONG:
399
0
            pComparator.reset( new ScalarPredicateLess< sal_Int32 > );
400
0
            break;
401
0
        case TypeClass_UNSIGNED_LONG:
402
0
            pComparator.reset( new ScalarPredicateLess< sal_uInt32 > );
403
0
            break;
404
0
        case TypeClass_HYPER:
405
0
            pComparator.reset( new ScalarPredicateLess< sal_Int64 > );
406
0
            break;
407
0
        case TypeClass_UNSIGNED_HYPER:
408
0
            pComparator.reset( new ScalarPredicateLess< sal_uInt64 > );
409
0
            break;
410
0
        case TypeClass_FLOAT:
411
0
            pComparator.reset( new ScalarPredicateLess< float > );
412
0
            break;
413
0
        case TypeClass_DOUBLE:
414
0
            pComparator.reset( new ScalarPredicateLess< double > );
415
0
            break;
416
0
        case TypeClass_STRING:
417
0
            if ( i_collator.is() )
418
0
                pComparator.reset( new StringCollationPredicateLess( i_collator ) );
419
0
            else
420
0
                pComparator.reset( new StringPredicateLess );
421
0
            break;
422
0
        case TypeClass_TYPE:
423
0
            pComparator.reset( new TypePredicateLess );
424
0
            break;
425
0
        case TypeClass_ENUM:
426
0
            pComparator.reset( new EnumPredicateLess( i_type ) );
427
0
            break;
428
0
        case TypeClass_INTERFACE:
429
0
            pComparator.reset( new InterfacePredicateLess );
430
0
            break;
431
0
        case TypeClass_STRUCT:
432
0
            if ( i_type.equals( ::cppu::UnoType< Date >::get() ) )
433
0
                pComparator.reset( new DatePredicateLess );
434
0
            else if ( i_type.equals( ::cppu::UnoType< Time >::get() ) )
435
0
                pComparator.reset( new TimePredicateLess );
436
0
            else if ( i_type.equals( ::cppu::UnoType< DateTime >::get() ) )
437
0
                pComparator.reset( new DateTimePredicateLess );
438
0
            break;
439
0
        default:
440
0
            break;
441
0
        }
442
0
        return pComparator;
443
0
    }
444
445
    bool anyLess( css::uno::Any const & lhs, css::uno::Any const & rhs)
446
270k
    {
447
270k
        return anyLess( lhs.getValue(), lhs.getValueTypeRef(), rhs.getValue(), rhs.getValueTypeRef());
448
270k
    }
449
450
} // namespace comphelper
451
452
453
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */