Coverage Report

Created: 2025-11-16 09:57

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.57G
        {
48
1.57G
            return x.aProperty.Handle < y.aProperty.Handle;
49
1.57G
        }
50
    };
51
    // comparing two property descriptions (by name)
52
    struct PropertyDescriptionNameMatch
53
    {
54
        OUString const m_rCompare;
55
54.6k
        explicit PropertyDescriptionNameMatch( OUString _aCompare ) : m_rCompare(std::move( _aCompare )) { }
56
57
        bool operator() (const PropertyDescription& x ) const
58
203k
        {
59
203k
            return x.aProperty.Name == m_rCompare;
60
203k
        }
61
    };
62
}
63
64
OPropertyContainerHelper::OPropertyContainerHelper()
65
3.62M
{
66
3.62M
}
67
68
69
OPropertyContainerHelper::~OPropertyContainerHelper()
70
3.62M
{
71
3.62M
}
72
73
74
void OPropertyContainerHelper::registerProperty(const OUString& _rName, sal_Int32 _nHandle,
75
        sal_Int32 _nAttributes, void* _pPointerToMember, const Type& _rMemberType)
76
53.6M
{
77
53.6M
    OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) == 0,
78
53.6M
        "OPropertyContainerHelper::registerProperty: don't use this for properties which may be void ! There is a method called \"registerMayBeVoidProperty\" for this !");
79
53.6M
    OSL_ENSURE(!_rMemberType.equals(cppu::UnoType<Any>::get()),
80
53.6M
        "OPropertyContainerHelper::registerProperty: don't give my the type of a uno::Any ! Really can't handle this !");
81
53.6M
    OSL_ENSURE(_pPointerToMember,
82
53.6M
        "OPropertyContainerHelper::registerProperty: you gave me nonsense : the pointer must be non-NULL");
83
84
53.6M
    PropertyDescription aNewProp;
85
53.6M
    aNewProp.aProperty = Property( _rName, _nHandle, _rMemberType, static_cast<sal_Int16>(_nAttributes) );
86
53.6M
    aNewProp.eLocated = PropertyDescription::LocationType::DerivedClassRealType;
87
53.6M
    aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
88
89
53.6M
    implPushBackProperty(aNewProp);
90
53.6M
}
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
2.86M
{
105
2.86M
    OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) != 0,
106
2.86M
        "OPropertyContainerHelper::registerMayBeVoidProperty: why calling this when the attributes say nothing about may-be-void ?");
107
2.86M
    OSL_ENSURE(!_rExpectedType.equals(cppu::UnoType<Any>::get()),
108
2.86M
        "OPropertyContainerHelper::registerMayBeVoidProperty: don't give my the type of a uno::Any ! Really can't handle this !");
109
2.86M
    OSL_ENSURE(_pPointerToMember,
110
2.86M
        "OPropertyContainerHelper::registerMayBeVoidProperty: you gave me nonsense : the pointer must be non-NULL");
111
112
2.86M
    _nAttributes |= PropertyAttribute::MAYBEVOID;
113
114
2.86M
    PropertyDescription aNewProp;
115
2.86M
    aNewProp.aProperty = Property( _rName, _nHandle, _rExpectedType, static_cast<sal_Int16>(_nAttributes) );
116
2.86M
    aNewProp.eLocated = PropertyDescription::LocationType::DerivedClassAnyType;
117
2.86M
    aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
118
119
2.86M
    implPushBackProperty(aNewProp);
120
2.86M
}
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
58.8k
{
126
58.8k
    OSL_ENSURE(!_rType.equals(cppu::UnoType<Any>::get()),
127
58.8k
        "OPropertyContainerHelper::registerPropertyNoMember : don't give my the type of a uno::Any ! Really can't handle this !");
128
58.8k
    OSL_ENSURE(
129
58.8k
        (_pInitialValue.isExtractableTo(_rType)
130
58.8k
         || (!_pInitialValue.hasValue()
131
58.8k
             && (_nAttributes & PropertyAttribute::MAYBEVOID) != 0)),
132
58.8k
        "bad initial value");
133
134
58.8k
    PropertyDescription aNewProp;
135
58.8k
    aNewProp.aProperty = Property( _rName, _nHandle, _rType, static_cast<sal_Int16>(_nAttributes) );
136
58.8k
    aNewProp.eLocated = PropertyDescription::LocationType::HoldMyself;
137
58.8k
    aNewProp.aLocation.nOwnClassVectorIndex = m_aHoldProperties.size();
138
58.8k
    m_aHoldProperties.push_back(_pInitialValue);
139
140
58.8k
    implPushBackProperty(aNewProp);
141
58.8k
}
142
143
144
bool OPropertyContainerHelper::isRegisteredProperty( sal_Int32 _nHandle ) const
145
10.3M
{
146
10.3M
    return const_cast< OPropertyContainerHelper* >( this )->searchHandle( _nHandle ) != m_aProperties.end();
147
10.3M
}
148
149
150
bool OPropertyContainerHelper::isRegisteredProperty( const OUString& _rName ) const
151
54.6k
{
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
54.6k
    return std::any_of(
158
54.6k
        m_aProperties.begin(),
159
54.6k
        m_aProperties.end(),
160
54.6k
        PropertyDescriptionNameMatch( _rName )
161
54.6k
    );
162
54.6k
}
163
164
165
namespace
166
{
167
    struct ComparePropertyHandles
168
    {
169
        bool operator()( const PropertyDescription& _rLHS, const PropertyDescription& _nRHS ) const
170
156M
        {
171
156M
            return _rLHS.aProperty.Handle < _nRHS.aProperty.Handle;
172
156M
        }
173
    };
174
}
175
176
177
void OPropertyContainerHelper::implPushBackProperty(const PropertyDescription& _rProp)
178
56.5M
{
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
56.5M
    PropertiesIterator pos = std::lower_bound(
188
56.5M
        m_aProperties.begin(), m_aProperties.end(),
189
56.5M
        _rProp, ComparePropertyHandles() );
190
191
56.5M
    m_aProperties.insert( pos, _rProp );
192
56.5M
}
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
2.82M
{
213
2.82M
    bool bModified = false;
214
215
    // get the property somebody is asking for
216
2.82M
    PropertiesIterator aPos = searchHandle(_nHandle);
217
2.82M
    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
2.82M
    switch (aPos->eLocated)
226
2.82M
    {
227
        // similar handling for the two cases where the value is stored in an any
228
0
        case PropertyDescription::LocationType::HoldMyself:
229
2.17M
        case PropertyDescription::LocationType::DerivedClassAnyType:
230
2.17M
        {
231
2.17M
            bool bMayBeVoid = ((aPos->aProperty.Attributes & PropertyAttribute::MAYBEVOID) != 0);
232
233
234
            // non modifiable version of the value-to-be-set
235
2.17M
            Any aNewRequestedValue( _rValue );
236
237
            // normalization
238
            // #i29490#
239
2.17M
            if ( !aNewRequestedValue.getValueType().equals( aPos->aProperty.Type ) )
240
1.54M
            {   // the actually given value is not of the same type as the one required
241
1.54M
                Any aProperlyTyped( nullptr, aPos->aProperty.Type.getTypeLibType() );
242
243
1.54M
                if (    uno_type_assignData(
244
1.54M
                            const_cast< void* >( aProperlyTyped.getValue() ), aProperlyTyped.getValueType().getTypeLibType(),
245
1.54M
                            const_cast< void* >( aNewRequestedValue.getValue() ), aNewRequestedValue.getValueType().getTypeLibType(),
246
1.54M
                            reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
247
1.54M
                            reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
248
1.54M
                            reinterpret_cast< uno_ReleaseFunc >( cpp_release )
249
1.54M
                        )
250
1.54M
                    )
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.54M
            }
257
258
            // argument check
259
2.17M
            if  (   !   (   (bMayBeVoid && !aNewRequestedValue.hasValue())                      // void is allowed if the attribute says so
260
627k
                        ||  (aNewRequestedValue.getValueType().equals(aPos->aProperty.Type))    // else the types have to be equal
261
2.17M
                        )
262
2.17M
                )
263
0
            {
264
0
                lcl_throwIllegalPropertyValueTypeException( *aPos, _rValue );
265
0
            }
266
267
2.17M
            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.17M
            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.17M
            else
279
2.17M
                pPropContainer = static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
280
281
            // check if the new value differs from the current one
282
2.17M
            if (!pPropContainer->hasValue() || !aNewRequestedValue.hasValue())
283
1.85M
                bModified = pPropContainer->hasValue() != aNewRequestedValue.hasValue();
284
318k
            else
285
318k
                bModified = !uno_type_equalData(
286
318k
                                const_cast< void* >( pPropContainer->getValue() ), aPos->aProperty.Type.getTypeLibType(),
287
318k
                                const_cast< void* >( aNewRequestedValue.getValue() ), aPos->aProperty.Type.getTypeLibType(),
288
318k
                                reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
289
318k
                                reinterpret_cast< uno_ReleaseFunc >( cpp_release )
290
318k
                            );
291
292
2.17M
            if (bModified)
293
318k
            {
294
318k
                _rOldValue = *pPropContainer;
295
318k
                _rConvertedValue = std::move(aNewRequestedValue);
296
318k
            }
297
2.17M
        }
298
2.17M
        break;
299
656k
        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
656k
            Any aProperlyTyped;
306
656k
            const Any* pNewValue = &_rValue;
307
308
656k
            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
656k
            OSL_ENSURE( pNewValue->getValueType() == aPos->aProperty.Type,
336
656k
                "OPropertyContainerHelper::convertFastPropertyValue: conversion failed!" );
337
656k
            bModified = !uno_type_equalData(
338
656k
                            aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type.getTypeLibType(),
339
656k
                            const_cast<void*>(pNewValue->getValue()), aPos->aProperty.Type.getTypeLibType(),
340
656k
                            reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
341
656k
                            reinterpret_cast< uno_ReleaseFunc >( cpp_release )
342
656k
                        );
343
344
656k
            if (bModified)
345
38.8k
            {
346
38.8k
                _rOldValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
347
38.8k
                _rConvertedValue = *pNewValue;
348
38.8k
            }
349
656k
            break;
350
2.82M
    }
351
352
2.82M
    return bModified;
353
2.82M
}
354
355
356
void OPropertyContainerHelper::setFastPropertyValue(sal_Int32 _nHandle, const Any& _rValue)
357
357k
{
358
    // get the property somebody is asking for
359
357k
    PropertiesIterator aPos = searchHandle(_nHandle);
360
357k
    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
357k
    bool bSuccess = true;
369
370
357k
    switch (aPos->eLocated)
371
357k
    {
372
0
        case PropertyDescription::LocationType::HoldMyself:
373
0
            m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex] = _rValue;
374
0
            break;
375
376
318k
        case PropertyDescription::LocationType::DerivedClassAnyType:
377
318k
            *static_cast< Any* >(aPos->aLocation.pDerivedClassMember) = _rValue;
378
318k
            break;
379
380
38.8k
        case PropertyDescription::LocationType::DerivedClassRealType:
381
            // copy the data from the to-be-set value
382
38.8k
            bSuccess = uno_type_assignData(
383
38.8k
                aPos->aLocation.pDerivedClassMember,        aPos->aProperty.Type.getTypeLibType(),
384
38.8k
                const_cast< void* >( _rValue.getValue() ),  _rValue.getValueType().getTypeLibType(),
385
38.8k
                reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
386
38.8k
                reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
387
38.8k
                reinterpret_cast< uno_ReleaseFunc >( cpp_release ) );
388
389
38.8k
            OSL_ENSURE( bSuccess,
390
38.8k
                "OPropertyContainerHelper::setFastPropertyValue: ooops... the value could not be assigned!");
391
392
38.8k
            break;
393
357k
    }
394
357k
}
395
396
void OPropertyContainerHelper::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
397
357M
{
398
    // get the property somebody is asking for
399
357M
    PropertiesIterator aPos = const_cast<OPropertyContainerHelper*>(this)->searchHandle(_nHandle);
400
357M
    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
357M
    switch (aPos->eLocated)
409
357M
    {
410
17.9k
        case PropertyDescription::LocationType::HoldMyself:
411
17.9k
            OSL_ENSURE(aPos->aLocation.nOwnClassVectorIndex < m_aHoldProperties.size(),
412
17.9k
                "OPropertyContainerHelper::convertFastPropertyValue: invalid position !");
413
17.9k
            _rValue = m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex];
414
17.9k
            break;
415
1.85M
        case PropertyDescription::LocationType::DerivedClassAnyType:
416
1.85M
            _rValue = *static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
417
1.85M
            break;
418
355M
        case PropertyDescription::LocationType::DerivedClassRealType:
419
355M
            _rValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
420
355M
            break;
421
357M
    }
422
357M
}
423
424
425
OPropertyContainerHelper::PropertiesIterator OPropertyContainerHelper::searchHandle(sal_Int32 _nHandle)
426
371M
{
427
371M
    PropertyDescription aHandlePropDesc;
428
371M
    aHandlePropDesc.aProperty.Handle = _nHandle;
429
    // search a lower bound
430
371M
    PropertiesIterator aLowerBound = std::lower_bound(
431
371M
        m_aProperties.begin(),
432
371M
        m_aProperties.end(),
433
371M
        aHandlePropDesc,
434
371M
        PropertyDescriptionHandleCompare());
435
436
    // check for identity
437
371M
    if ((aLowerBound != m_aProperties.end()) && aLowerBound->aProperty.Handle != _nHandle)
438
7.43M
        aLowerBound = m_aProperties.end();
439
440
371M
    return aLowerBound;
441
371M
}
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
124k
{
460
124k
    Sequence< Property > aOwnProps(m_aProperties.size());
461
124k
    Property* pOwnProps = aOwnProps.getArray();
462
463
124k
    for (const auto& rProp : m_aProperties)
464
1.92M
    {
465
1.92M
        pOwnProps->Name = rProp.aProperty.Name;
466
1.92M
        pOwnProps->Handle = rProp.aProperty.Handle;
467
1.92M
        pOwnProps->Attributes = rProp.aProperty.Attributes;
468
1.92M
        pOwnProps->Type = rProp.aProperty.Type;
469
1.92M
        ++pOwnProps;
470
1.92M
    }
471
472
    // as our property vector is sorted by handles, not by name, we have to sort aOwnProps
473
124k
    auto [begin, end] = asNonConstRange(aOwnProps);
474
124k
    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
124k
    Sequence< Property > aOutput(_rProps.getLength() + aOwnProps.getLength());
479
    // do the merge
480
124k
    std::merge(   std::cbegin(_rProps), std::cend(_rProps),       // input 1
481
124k
                  std::cbegin(aOwnProps), std::cend(aOwnProps),   // input 2
482
124k
                  aOutput.getArray(),                   // output
483
124k
                  PropertyCompareByName()               // compare operator
484
124k
              );
485
486
    // copy the output
487
124k
    _rProps = std::move(aOutput);
488
124k
}
489
490
491
}   // namespace comphelper
492
493
494
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */