Coverage Report

Created: 2025-07-07 10:01

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