Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/comphelper/source/property/propertycontainerhelper.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 <comphelper/propertycontainerhelper.hxx>
21
#include <comphelper/property.hxx>
22
#include <osl/diagnose.h>
23
#include <uno/data.h>
24
#include <com/sun/star/uno/Sequence.hxx>
25
#include <com/sun/star/beans/PropertyAttribute.hpp>
26
#include <com/sun/star/beans/UnknownPropertyException.hpp>
27
28
#include <algorithm>
29
#include <utility>
30
31
32
namespace comphelper
33
{
34
35
36
using namespace ::com::sun::star::uno;
37
using namespace ::com::sun::star::lang;
38
using namespace ::com::sun::star::beans;
39
40
41
namespace
42
{
43
    // comparing two property descriptions
44
    struct PropertyDescriptionHandleCompare
45
    {
46
        bool operator() (const PropertyDescription& x, const PropertyDescription& y) const
47
1.68G
        {
48
1.68G
            return x.aProperty.Handle < y.aProperty.Handle;
49
1.68G
        }
50
    };
51
    // comparing two property descriptions (by name)
52
    struct PropertyDescriptionNameMatch
53
    {
54
        OUString const m_rCompare;
55
60.1k
        explicit PropertyDescriptionNameMatch( OUString _aCompare ) : m_rCompare(std::move( _aCompare )) { }
56
57
        bool operator() (const PropertyDescription& x ) const
58
220k
        {
59
220k
            return x.aProperty.Name == m_rCompare;
60
220k
        }
61
    };
62
}
63
64
OPropertyContainerHelper::OPropertyContainerHelper()
65
4.13M
{
66
4.13M
}
67
68
69
OPropertyContainerHelper::~OPropertyContainerHelper()
70
4.12M
{
71
4.12M
}
72
73
74
void OPropertyContainerHelper::registerProperty(const OUString& _rName, sal_Int32 _nHandle,
75
        sal_Int32 _nAttributes, void* _pPointerToMember, const Type& _rMemberType)
76
61.0M
{
77
61.0M
    OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) == 0,
78
61.0M
        "OPropertyContainerHelper::registerProperty: don't use this for properties which may be void ! There is a method called \"registerMayBeVoidProperty\" for this !");
79
61.0M
    OSL_ENSURE(!_rMemberType.equals(cppu::UnoType<Any>::get()),
80
61.0M
        "OPropertyContainerHelper::registerProperty: don't give my the type of a uno::Any ! Really can't handle this !");
81
61.0M
    OSL_ENSURE(_pPointerToMember,
82
61.0M
        "OPropertyContainerHelper::registerProperty: you gave me nonsense : the pointer must be non-NULL");
83
84
61.0M
    PropertyDescription aNewProp;
85
61.0M
    aNewProp.aProperty = Property( _rName, _nHandle, _rMemberType, static_cast<sal_Int16>(_nAttributes) );
86
61.0M
    aNewProp.eLocated = PropertyDescription::LocationType::DerivedClassRealType;
87
61.0M
    aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
88
89
61.0M
    implPushBackProperty(aNewProp);
90
61.0M
}
91
92
93
void OPropertyContainerHelper::revokeProperty( sal_Int32 _nHandle )
94
0
{
95
0
    PropertiesIterator aPos = searchHandle( _nHandle );
96
0
    if ( aPos == m_aProperties.end() )
97
0
        throw UnknownPropertyException(OUString::number(_nHandle));
98
0
    m_aProperties.erase( aPos );
99
0
}
100
101
102
void OPropertyContainerHelper::registerMayBeVoidProperty(const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes,
103
        Any* _pPointerToMember, const Type& _rExpectedType)
104
3.24M
{
105
3.24M
    OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) != 0,
106
3.24M
        "OPropertyContainerHelper::registerMayBeVoidProperty: why calling this when the attributes say nothing about may-be-void ?");
107
3.24M
    OSL_ENSURE(!_rExpectedType.equals(cppu::UnoType<Any>::get()),
108
3.24M
        "OPropertyContainerHelper::registerMayBeVoidProperty: don't give my the type of a uno::Any ! Really can't handle this !");
109
3.24M
    OSL_ENSURE(_pPointerToMember,
110
3.24M
        "OPropertyContainerHelper::registerMayBeVoidProperty: you gave me nonsense : the pointer must be non-NULL");
111
112
3.24M
    _nAttributes |= PropertyAttribute::MAYBEVOID;
113
114
3.24M
    PropertyDescription aNewProp;
115
3.24M
    aNewProp.aProperty = Property( _rName, _nHandle, _rExpectedType, static_cast<sal_Int16>(_nAttributes) );
116
3.24M
    aNewProp.eLocated = PropertyDescription::LocationType::DerivedClassAnyType;
117
3.24M
    aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
118
119
3.24M
    implPushBackProperty(aNewProp);
120
3.24M
}
121
122
123
void OPropertyContainerHelper::registerPropertyNoMember(const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes,
124
        const Type& _rType, css::uno::Any const & _pInitialValue)
125
67.0k
{
126
67.0k
    OSL_ENSURE(!_rType.equals(cppu::UnoType<Any>::get()),
127
67.0k
        "OPropertyContainerHelper::registerPropertyNoMember : don't give my the type of a uno::Any ! Really can't handle this !");
128
67.0k
    OSL_ENSURE(
129
67.0k
        (_pInitialValue.isExtractableTo(_rType)
130
67.0k
         || (!_pInitialValue.hasValue()
131
67.0k
             && (_nAttributes & PropertyAttribute::MAYBEVOID) != 0)),
132
67.0k
        "bad initial value");
133
134
67.0k
    PropertyDescription aNewProp;
135
67.0k
    aNewProp.aProperty = Property( _rName, _nHandle, _rType, static_cast<sal_Int16>(_nAttributes) );
136
67.0k
    aNewProp.eLocated = PropertyDescription::LocationType::HoldMyself;
137
67.0k
    aNewProp.aLocation.nOwnClassVectorIndex = m_aHoldProperties.size();
138
67.0k
    m_aHoldProperties.push_back(_pInitialValue);
139
140
67.0k
    implPushBackProperty(aNewProp);
141
67.0k
}
142
143
144
bool OPropertyContainerHelper::isRegisteredProperty( sal_Int32 _nHandle ) const
145
11.7M
{
146
11.7M
    return const_cast< OPropertyContainerHelper* >( this )->searchHandle( _nHandle ) != m_aProperties.end();
147
11.7M
}
148
149
150
bool OPropertyContainerHelper::isRegisteredProperty( const OUString& _rName ) const
151
60.1k
{
152
    // TODO: the current structure is from a time where properties were
153
    // static, not dynamic. Since we allow that properties are also dynamic,
154
    // i.e. registered and revoked even though the XPropertySet has already been
155
    // accessed, a vector is not really the best data structure anymore ...
156
157
60.1k
    return std::any_of(
158
60.1k
        m_aProperties.begin(),
159
60.1k
        m_aProperties.end(),
160
60.1k
        PropertyDescriptionNameMatch( _rName )
161
60.1k
    );
162
60.1k
}
163
164
165
namespace
166
{
167
    struct ComparePropertyHandles
168
    {
169
        bool operator()( const PropertyDescription& _rLHS, const PropertyDescription& _nRHS ) const
170
177M
        {
171
177M
            return _rLHS.aProperty.Handle < _nRHS.aProperty.Handle;
172
177M
        }
173
    };
174
}
175
176
177
void OPropertyContainerHelper::implPushBackProperty(const PropertyDescription& _rProp)
178
64.3M
{
179
#ifdef DBG_UTIL
180
    for (const auto& checkConflicts : m_aProperties)
181
    {
182
        OSL_ENSURE(checkConflicts.aProperty.Name != _rProp.aProperty.Name, "OPropertyContainerHelper::implPushBackProperty: name already exists!");
183
        OSL_ENSURE(checkConflicts.aProperty.Handle != _rProp.aProperty.Handle, "OPropertyContainerHelper::implPushBackProperty: handle already exists!");
184
    }
185
#endif
186
187
64.3M
    PropertiesIterator pos = std::lower_bound(
188
64.3M
        m_aProperties.begin(), m_aProperties.end(),
189
64.3M
        _rProp, ComparePropertyHandles() );
190
191
64.3M
    m_aProperties.insert( pos, _rProp );
192
64.3M
}
193
194
195
namespace
196
{
197
    void lcl_throwIllegalPropertyValueTypeException( const PropertyDescription& _rProperty, const Any& _rValue )
198
0
    {
199
0
        throw IllegalArgumentException(
200
0
            "The given value cannot be converted to the required property type."
201
0
            " (property name \"" +  _rProperty.aProperty.Name
202
0
            + "\", found value type \"" + _rValue.getValueTypeName()
203
0
            + "\", required property type \"" + _rProperty.aProperty.Type.getTypeName()
204
0
            + "\")",
205
0
            nullptr, 4 );
206
0
    }
207
}
208
209
210
bool OPropertyContainerHelper::convertFastPropertyValue(
211
    Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
212
3.21M
{
213
3.21M
    bool bModified = false;
214
215
    // get the property somebody is asking for
216
3.21M
    PropertiesIterator aPos = searchHandle(_nHandle);
217
3.21M
    if (aPos == m_aProperties.end())
218
0
    {
219
0
        OSL_FAIL( "OPropertyContainerHelper::convertFastPropertyValue: unknown handle!" );
220
        // should not happen if the derived class has built a correct property set info helper to be used by
221
        // our base class OPropertySetHelper
222
0
        return bModified;
223
0
    }
224
225
3.21M
    switch (aPos->eLocated)
226
3.21M
    {
227
        // similar handling for the two cases where the value is stored in an any
228
0
        case PropertyDescription::LocationType::HoldMyself:
229
2.46M
        case PropertyDescription::LocationType::DerivedClassAnyType:
230
2.46M
        {
231
2.46M
            bool bMayBeVoid = ((aPos->aProperty.Attributes & PropertyAttribute::MAYBEVOID) != 0);
232
233
234
            // non modifiable version of the value-to-be-set
235
2.46M
            Any aNewRequestedValue( _rValue );
236
237
            // normalization
238
            // #i29490#
239
2.46M
            if ( !aNewRequestedValue.getValueType().equals( aPos->aProperty.Type ) )
240
1.75M
            {   // the actually given value is not of the same type as the one required
241
1.75M
                Any aProperlyTyped( nullptr, aPos->aProperty.Type.getTypeLibType() );
242
243
1.75M
                if (    uno_type_assignData(
244
1.75M
                            const_cast< void* >( aProperlyTyped.getValue() ), aProperlyTyped.getValueType().getTypeLibType(),
245
1.75M
                            const_cast< void* >( aNewRequestedValue.getValue() ), aNewRequestedValue.getValueType().getTypeLibType(),
246
1.75M
                            reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
247
1.75M
                            reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
248
1.75M
                            reinterpret_cast< uno_ReleaseFunc >( cpp_release )
249
1.75M
                        )
250
1.75M
                    )
251
0
                {
252
                    // we were able to query the given XInterface-derivee for the interface
253
                    // which is required for this property
254
0
                    aNewRequestedValue = std::move(aProperlyTyped);
255
0
                }
256
1.75M
            }
257
258
            // argument check
259
2.46M
            if  (   !   (   (bMayBeVoid && !aNewRequestedValue.hasValue())                      // void is allowed if the attribute says so
260
713k
                        ||  (aNewRequestedValue.getValueType().equals(aPos->aProperty.Type))    // else the types have to be equal
261
2.46M
                        )
262
2.46M
                )
263
0
            {
264
0
                lcl_throwIllegalPropertyValueTypeException( *aPos, _rValue );
265
0
            }
266
267
2.46M
            Any* pPropContainer = nullptr;
268
                // the pointer to the any which holds the property value, no matter if located in the derived class
269
                // or in out vector
270
271
2.46M
            if (PropertyDescription::LocationType::HoldMyself == aPos->eLocated)
272
0
            {
273
0
                OSL_ENSURE(aPos->aLocation.nOwnClassVectorIndex < m_aHoldProperties.size(),
274
0
                    "OPropertyContainerHelper::convertFastPropertyValue: invalid position !");
275
0
                auto aIter = m_aHoldProperties.begin() + aPos->aLocation.nOwnClassVectorIndex;
276
0
                pPropContainer = &(*aIter);
277
0
            }
278
2.46M
            else
279
2.46M
                pPropContainer = static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
280
281
            // check if the new value differs from the current one
282
2.46M
            if (!pPropContainer->hasValue() || !aNewRequestedValue.hasValue())
283
2.10M
                bModified = pPropContainer->hasValue() != aNewRequestedValue.hasValue();
284
362k
            else
285
362k
                bModified = !uno_type_equalData(
286
362k
                                const_cast< void* >( pPropContainer->getValue() ), aPos->aProperty.Type.getTypeLibType(),
287
362k
                                const_cast< void* >( aNewRequestedValue.getValue() ), aPos->aProperty.Type.getTypeLibType(),
288
362k
                                reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
289
362k
                                reinterpret_cast< uno_ReleaseFunc >( cpp_release )
290
362k
                            );
291
292
2.46M
            if (bModified)
293
362k
            {
294
362k
                _rOldValue = *pPropContainer;
295
362k
                _rConvertedValue = std::move(aNewRequestedValue);
296
362k
            }
297
2.46M
        }
298
2.46M
        break;
299
748k
        case PropertyDescription::LocationType::DerivedClassRealType:
300
            // let the UNO runtime library do any possible conversion
301
            // this may include a change of the type - for instance, if a LONG is required,
302
            // but a short is given, then this is valid, as it can be converted without any potential
303
            // data loss
304
305
748k
            Any aProperlyTyped;
306
748k
            const Any* pNewValue = &_rValue;
307
308
748k
            if (!_rValue.getValueType().equals(aPos->aProperty.Type))
309
0
            {
310
0
                bool bConverted = false;
311
312
                // a temporary any of the correct (required) type
313
0
                aProperlyTyped = Any( nullptr, aPos->aProperty.Type.getTypeLibType() );
314
                    // (need this as we do not want to overwrite the derived class member here)
315
316
0
                if (    uno_type_assignData(
317
0
                            const_cast<void*>(aProperlyTyped.getValue()), aProperlyTyped.getValueType().getTypeLibType(),
318
0
                            const_cast<void*>(_rValue.getValue()), _rValue.getValueType().getTypeLibType(),
319
0
                            reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
320
0
                            reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
321
0
                            reinterpret_cast< uno_ReleaseFunc >( cpp_release )
322
0
                        )
323
0
                    )
324
0
                {
325
                    // could query for the requested interface
326
0
                    bConverted = true;
327
0
                    pNewValue = &aProperlyTyped;
328
0
                }
329
330
0
                if ( !bConverted )
331
0
                    lcl_throwIllegalPropertyValueTypeException( *aPos, _rValue );
332
0
            }
333
334
            // from here on, we should have the proper type
335
748k
            OSL_ENSURE( pNewValue->getValueType() == aPos->aProperty.Type,
336
748k
                "OPropertyContainerHelper::convertFastPropertyValue: conversion failed!" );
337
748k
            bModified = !uno_type_equalData(
338
748k
                            aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type.getTypeLibType(),
339
748k
                            const_cast<void*>(pNewValue->getValue()), aPos->aProperty.Type.getTypeLibType(),
340
748k
                            reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
341
748k
                            reinterpret_cast< uno_ReleaseFunc >( cpp_release )
342
748k
                        );
343
344
748k
            if (bModified)
345
46.8k
            {
346
46.8k
                _rOldValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
347
46.8k
                _rConvertedValue = *pNewValue;
348
46.8k
            }
349
748k
            break;
350
3.21M
    }
351
352
3.21M
    return bModified;
353
3.21M
}
354
355
356
void OPropertyContainerHelper::setFastPropertyValue(sal_Int32 _nHandle, const Any& _rValue)
357
409k
{
358
    // get the property somebody is asking for
359
409k
    PropertiesIterator aPos = searchHandle(_nHandle);
360
409k
    if (aPos == m_aProperties.end())
361
0
    {
362
0
        OSL_FAIL( "OPropertyContainerHelper::setFastPropertyValue: unknown handle!" );
363
        // should not happen if the derived class has built a correct property set info helper to be used by
364
        // our base class OPropertySetHelper
365
0
        return;
366
0
    }
367
368
409k
    bool bSuccess = true;
369
370
409k
    switch (aPos->eLocated)
371
409k
    {
372
0
        case PropertyDescription::LocationType::HoldMyself:
373
0
            m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex] = _rValue;
374
0
            break;
375
376
362k
        case PropertyDescription::LocationType::DerivedClassAnyType:
377
362k
            *static_cast< Any* >(aPos->aLocation.pDerivedClassMember) = _rValue;
378
362k
            break;
379
380
46.8k
        case PropertyDescription::LocationType::DerivedClassRealType:
381
            // copy the data from the to-be-set value
382
46.8k
            bSuccess = uno_type_assignData(
383
46.8k
                aPos->aLocation.pDerivedClassMember,        aPos->aProperty.Type.getTypeLibType(),
384
46.8k
                const_cast< void* >( _rValue.getValue() ),  _rValue.getValueType().getTypeLibType(),
385
46.8k
                reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
386
46.8k
                reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
387
46.8k
                reinterpret_cast< uno_ReleaseFunc >( cpp_release ) );
388
389
46.8k
            OSL_ENSURE( bSuccess,
390
46.8k
                "OPropertyContainerHelper::setFastPropertyValue: ooops... the value could not be assigned!");
391
392
46.8k
            break;
393
409k
    }
394
409k
}
395
396
void OPropertyContainerHelper::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
397
382M
{
398
    // get the property somebody is asking for
399
382M
    PropertiesIterator aPos = const_cast<OPropertyContainerHelper*>(this)->searchHandle(_nHandle);
400
382M
    if (aPos == m_aProperties.end())
401
0
    {
402
0
        assert( false && "OPropertyContainerHelper::getFastPropertyValue: unknown handle" );
403
        // should not happen if the derived class has built a correct property set info helper to be used by
404
        // our base class OPropertySetHelper
405
0
        return;
406
0
    }
407
408
382M
    switch (aPos->eLocated)
409
382M
    {
410
21.5k
        case PropertyDescription::LocationType::HoldMyself:
411
21.5k
            OSL_ENSURE(aPos->aLocation.nOwnClassVectorIndex < m_aHoldProperties.size(),
412
21.5k
                "OPropertyContainerHelper::convertFastPropertyValue: invalid position !");
413
21.5k
            _rValue = m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex];
414
21.5k
            break;
415
2.10M
        case PropertyDescription::LocationType::DerivedClassAnyType:
416
2.10M
            _rValue = *static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
417
2.10M
            break;
418
380M
        case PropertyDescription::LocationType::DerivedClassRealType:
419
380M
            _rValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
420
380M
            break;
421
382M
    }
422
382M
}
423
424
425
OPropertyContainerHelper::PropertiesIterator OPropertyContainerHelper::searchHandle(sal_Int32 _nHandle)
426
398M
{
427
398M
    PropertyDescription aHandlePropDesc;
428
398M
    aHandlePropDesc.aProperty.Handle = _nHandle;
429
    // search a lower bound
430
398M
    PropertiesIterator aLowerBound = std::lower_bound(
431
398M
        m_aProperties.begin(),
432
398M
        m_aProperties.end(),
433
398M
        aHandlePropDesc,
434
398M
        PropertyDescriptionHandleCompare());
435
436
    // check for identity
437
398M
    if ((aLowerBound != m_aProperties.end()) && aLowerBound->aProperty.Handle != _nHandle)
438
8.43M
        aLowerBound = m_aProperties.end();
439
440
398M
    return aLowerBound;
441
398M
}
442
443
444
const Property& OPropertyContainerHelper::getProperty( const OUString& _rName ) const
445
0
{
446
0
    ConstPropertiesIterator pos = std::find_if(
447
0
        m_aProperties.begin(),
448
0
        m_aProperties.end(),
449
0
        PropertyDescriptionNameMatch( _rName )
450
0
    );
451
0
    if ( pos == m_aProperties.end() )
452
0
        throw UnknownPropertyException( _rName );
453
454
0
    return pos->aProperty;
455
0
}
456
457
458
void OPropertyContainerHelper::describeProperties(Sequence< Property >& _rProps) const
459
148k
{
460
148k
    Sequence< Property > aOwnProps(m_aProperties.size());
461
148k
    Property* pOwnProps = aOwnProps.getArray();
462
463
148k
    for (const auto& rProp : m_aProperties)
464
2.30M
    {
465
2.30M
        pOwnProps->Name = rProp.aProperty.Name;
466
2.30M
        pOwnProps->Handle = rProp.aProperty.Handle;
467
2.30M
        pOwnProps->Attributes = rProp.aProperty.Attributes;
468
2.30M
        pOwnProps->Type = rProp.aProperty.Type;
469
2.30M
        ++pOwnProps;
470
2.30M
    }
471
472
    // as our property vector is sorted by handles, not by name, we have to sort aOwnProps
473
148k
    auto [begin, end] = asNonConstRange(aOwnProps);
474
148k
    std::sort(begin, end, PropertyCompareByName());
475
476
    // unfortunately the STL merge function does not allow the output range to overlap one of the input ranges,
477
    // so we need an extra sequence
478
148k
    Sequence< Property > aOutput(_rProps.getLength() + aOwnProps.getLength());
479
    // do the merge
480
148k
    std::merge(   std::cbegin(_rProps), std::cend(_rProps),       // input 1
481
148k
                  std::cbegin(aOwnProps), std::cend(aOwnProps),   // input 2
482
148k
                  aOutput.getArray(),                   // output
483
148k
                  PropertyCompareByName()               // compare operator
484
148k
              );
485
486
    // copy the output
487
148k
    _rProps = std::move(aOutput);
488
148k
}
489
490
491
}   // namespace comphelper
492
493
494
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */