Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/comphelper/source/property/propshlp.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 <osl/diagnose.h>
21
#include <cppuhelper/implbase.hxx>
22
#include <cppuhelper/queryinterface.hxx>
23
#include <comphelper/propshlp.hxx>
24
#include <cppuhelper/exc_hlp.hxx>
25
#include <cppuhelper/propshlp.hxx>
26
#include <com/sun/star/beans/PropertyAttribute.hpp>
27
#include <com/sun/star/lang/DisposedException.hpp>
28
#include <com/sun/star/lang/IllegalArgumentException.hpp>
29
#include <memory>
30
#include <sal/log.hxx>
31
32
using namespace com::sun::star::uno;
33
using namespace com::sun::star::beans;
34
using namespace com::sun::star::lang;
35
using namespace cppu;
36
37
namespace comphelper
38
{
39
extern "C" {
40
41
static int compare_OUString_Property_Impl(const void* arg1, const void* arg2) noexcept
42
165
{
43
165
    return static_cast<OUString const*>(arg1)->compareTo(static_cast<Property const*>(arg2)->Name);
44
165
}
45
}
46
47
/**
48
 * The class which implements the PropertySetInfo interface.
49
 */
50
51
namespace
52
{
53
class OPropertySetHelperInfo_Impl : public WeakImplHelper<css::beans::XPropertySetInfo>
54
{
55
    Sequence<Property> aInfos;
56
57
public:
58
    explicit OPropertySetHelperInfo_Impl(IPropertyArrayHelper& rHelper_);
59
60
    // XPropertySetInfo-methods
61
    virtual Sequence<Property> SAL_CALL getProperties() override;
62
    virtual Property SAL_CALL getPropertyByName(const OUString& PropertyName) override;
63
    virtual sal_Bool SAL_CALL hasPropertyByName(const OUString& PropertyName) override;
64
};
65
}
66
67
/**
68
 * Create an object that implements XPropertySetInfo IPropertyArrayHelper.
69
 */
70
OPropertySetHelperInfo_Impl::OPropertySetHelperInfo_Impl(IPropertyArrayHelper& rHelper_)
71
1
    : aInfos(rHelper_.getProperties())
72
1
{
73
1
}
74
75
/**
76
 * Return the sequence of properties, which are provided through the constructor.
77
 */
78
55
Sequence<Property> OPropertySetHelperInfo_Impl::getProperties() { return aInfos; }
79
80
/**
81
 * Return the sequence of properties, which are provided through the constructor.
82
 */
83
Property OPropertySetHelperInfo_Impl::getPropertyByName(const OUString& PropertyName)
84
0
{
85
0
    Property* pR
86
0
        = static_cast<Property*>(bsearch(&PropertyName, aInfos.getConstArray(), aInfos.getLength(),
87
0
                                         sizeof(Property), compare_OUString_Property_Impl));
88
0
    if (!pR)
89
0
        throw UnknownPropertyException(PropertyName);
90
91
0
    return *pR;
92
0
}
93
94
/**
95
 * Return the sequence of properties, which are provided through the constructor.
96
 */
97
sal_Bool OPropertySetHelperInfo_Impl::hasPropertyByName(const OUString& PropertyName)
98
55
{
99
55
    Property* pR
100
55
        = static_cast<Property*>(bsearch(&PropertyName, aInfos.getConstArray(), aInfos.getLength(),
101
55
                                         sizeof(Property), compare_OUString_Property_Impl));
102
55
    return pR != nullptr;
103
55
}
104
105
55
OPropertySetHelper::OPropertySetHelper() {}
106
107
OPropertySetHelper::OPropertySetHelper(bool bIgnoreRuntimeExceptionsWhileFiring)
108
0
    : m_bIgnoreRuntimeExceptionsWhileFiring(bIgnoreRuntimeExceptionsWhileFiring)
109
0
{
110
0
}
111
112
/**
113
 * You must call disposing before.
114
 */
115
55
OPropertySetHelper::~OPropertySetHelper() {}
116
117
// XInterface
118
Any OPropertySetHelper::queryInterface(const css::uno::Type& rType)
119
110
{
120
110
    return ::cppu::queryInterface(rType, static_cast<XPropertySet*>(this),
121
110
                                  static_cast<XMultiPropertySet*>(this),
122
110
                                  static_cast<XFastPropertySet*>(this));
123
110
}
124
125
/**
126
 * called from the derivee's XTypeProvider::getTypes implementation
127
 */
128
css::uno::Sequence<css::uno::Type> OPropertySetHelper::getTypes()
129
0
{
130
0
    return { UnoType<css::beans::XPropertySet>::get(),
131
0
             UnoType<css::beans::XMultiPropertySet>::get(),
132
0
             UnoType<css::beans::XFastPropertySet>::get() };
133
0
}
134
135
// ComponentHelper
136
void OPropertySetHelper::disposing(std::unique_lock<std::mutex>& rGuard)
137
0
{
138
    // Create an event with this as sender
139
0
    Reference<XPropertySet> rSource = this;
140
0
    EventObject aEvt;
141
0
    aEvt.Source = rSource;
142
143
    // inform all listeners to release this object
144
    // The listener containers are automatically cleared
145
0
    aBoundLC.disposeAndClear(rGuard, aEvt);
146
0
    aVetoableLC.disposeAndClear(rGuard, aEvt);
147
0
}
148
149
Reference<XPropertySetInfo>
150
OPropertySetHelper::createPropertySetInfo(IPropertyArrayHelper& rProperties)
151
1
{
152
1
    return new OPropertySetHelperInfo_Impl(rProperties);
153
1
}
154
155
// XPropertySet
156
void OPropertySetHelper::setPropertyValue(const OUString& rPropertyName, const Any& rValue)
157
0
{
158
    // get the map table
159
0
    IPropertyArrayHelper& rPH = getInfoHelper();
160
    // map the name to the handle
161
0
    sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
162
0
    std::unique_lock aGuard(m_aMutex);
163
0
    setFastPropertyValueImpl(aGuard, nHandle, rValue);
164
0
}
165
166
// XPropertySet
167
Any OPropertySetHelper::getPropertyValue(const OUString& rPropertyName)
168
0
{
169
0
    std::unique_lock aGuard(m_aMutex);
170
0
    return getPropertyValueImpl(aGuard, rPropertyName);
171
0
}
172
173
Any OPropertySetHelper::getPropertyValueImpl(std::unique_lock<std::mutex>& rGuard,
174
                                             const OUString& rPropertyName)
175
0
{
176
    // get the map table
177
0
    IPropertyArrayHelper& rPH = getInfoHelper();
178
    // map the name to the handle
179
0
    sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
180
0
    if (nHandle == -1)
181
0
        throw UnknownPropertyException(rPropertyName);
182
    // call the method of the XFastPropertySet interface
183
0
    Any aAny;
184
0
    getFastPropertyValue(rGuard, aAny, nHandle);
185
0
    return aAny;
186
0
}
187
188
// XPropertySet
189
void OPropertySetHelper::addPropertyChangeListener(
190
    const OUString& rPropertyName, const Reference<XPropertyChangeListener>& rxListener)
191
0
{
192
0
    std::unique_lock aGuard(m_aMutex);
193
0
    OSL_ENSURE(!m_bDisposed, "object is disposed");
194
0
    if (m_bDisposed)
195
0
        return;
196
197
    // only add listeners if you are not disposed
198
    // a listener with no name means all properties
199
0
    if (!rPropertyName.isEmpty())
200
0
    {
201
        // get the map table
202
0
        IPropertyArrayHelper& rPH = getInfoHelper();
203
        // map the name to the handle
204
0
        sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
205
0
        if (nHandle == -1)
206
0
        {
207
            // property not known throw exception
208
0
            throw UnknownPropertyException(rPropertyName);
209
0
        }
210
211
0
        sal_Int16 nAttributes;
212
0
        rPH.fillPropertyMembersByHandle(nullptr, &nAttributes, nHandle);
213
0
        if (!(nAttributes & css::beans::PropertyAttribute::BOUND))
214
0
        {
215
0
            OSL_FAIL("add listener to an unbound property");
216
            // silent ignore this
217
0
            return;
218
0
        }
219
        // add the change listener to the helper container
220
0
        aBoundLC.addInterface(aGuard, nHandle, rxListener);
221
0
    }
222
0
    else
223
        // add the change listener to the helper container
224
0
        maPropertyChangeListeners.addInterface(aGuard, rxListener);
225
0
}
226
227
// XPropertySet
228
void OPropertySetHelper::removePropertyChangeListener(
229
    const OUString& rPropertyName, const Reference<XPropertyChangeListener>& rxListener)
230
0
{
231
0
    std::unique_lock aGuard(m_aMutex);
232
0
    OSL_ENSURE(!m_bDisposed, "object is disposed");
233
    // all listeners are automatically released in a dispose call
234
0
    if (m_bDisposed)
235
0
        return;
236
237
0
    if (!rPropertyName.isEmpty())
238
0
    {
239
        // get the map table
240
0
        IPropertyArrayHelper& rPH = getInfoHelper();
241
        // map the name to the handle
242
0
        sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
243
0
        if (nHandle == -1)
244
            // property not known throw exception
245
0
            throw UnknownPropertyException(rPropertyName);
246
0
        aBoundLC.removeInterface(aGuard, nHandle, rxListener);
247
0
    }
248
0
    else
249
0
    {
250
        // remove the change listener to the helper container
251
0
        maPropertyChangeListeners.removeInterface(aGuard, rxListener);
252
0
    }
253
0
}
254
255
// XPropertySet
256
void OPropertySetHelper::addVetoableChangeListener(
257
    const OUString& rPropertyName, const Reference<XVetoableChangeListener>& rxListener)
258
0
{
259
0
    std::unique_lock aGuard(m_aMutex);
260
0
    OSL_ENSURE(!m_bDisposed, "object is disposed");
261
0
    if (m_bDisposed)
262
0
        return;
263
264
    // only add listeners if you are not disposed
265
    // a listener with no name means all properties
266
0
    if (!rPropertyName.isEmpty())
267
0
    {
268
        // get the map table
269
0
        IPropertyArrayHelper& rPH = getInfoHelper();
270
        // map the name to the handle
271
0
        sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
272
0
        if (nHandle == -1)
273
0
        {
274
            // property not known throw exception
275
0
            throw UnknownPropertyException(rPropertyName);
276
0
        }
277
278
0
        sal_Int16 nAttributes;
279
0
        rPH.fillPropertyMembersByHandle(nullptr, &nAttributes, nHandle);
280
0
        if (!(nAttributes & PropertyAttribute::CONSTRAINED))
281
0
        {
282
0
            OSL_FAIL("addVetoableChangeListener, and property is not constrained");
283
            // silent ignore this
284
0
            return;
285
0
        }
286
        // add the vetoable listener to the helper container
287
0
        aVetoableLC.addInterface(aGuard, nHandle, rxListener);
288
0
    }
289
0
    else
290
        // add the vetoable listener to the helper container
291
0
        maVetoableChangeListeners.addInterface(aGuard, rxListener);
292
0
}
293
294
// XPropertySet
295
void OPropertySetHelper::removeVetoableChangeListener(
296
    const OUString& rPropertyName, const Reference<XVetoableChangeListener>& rxListener)
297
0
{
298
0
    std::unique_lock aGuard(m_aMutex);
299
0
    OSL_ENSURE(!m_bDisposed, "object is disposed");
300
    // all listeners are automatically released in a dispose call
301
0
    if (m_bDisposed)
302
0
        return;
303
304
0
    if (!rPropertyName.isEmpty())
305
0
    {
306
        // get the map table
307
0
        IPropertyArrayHelper& rPH = getInfoHelper();
308
        // map the name to the handle
309
0
        sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
310
0
        if (nHandle == -1)
311
0
        {
312
            // property not known throw exception
313
0
            throw UnknownPropertyException(rPropertyName);
314
0
        }
315
        // remove the vetoable listener to the helper container
316
0
        aVetoableLC.removeInterface(aGuard, nHandle, rxListener);
317
0
    }
318
0
    else
319
        // add the vetoable listener to the helper container
320
0
        maVetoableChangeListeners.removeInterface(aGuard, rxListener);
321
0
}
322
323
void OPropertySetHelper::setDependentFastPropertyValue(std::unique_lock<std::mutex>& rGuard,
324
                                                       sal_Int32 i_handle,
325
                                                       const css::uno::Any& i_value)
326
0
{
327
0
    sal_Int16 nAttributes(0);
328
0
    IPropertyArrayHelper& rInfo = getInfoHelper();
329
0
    if (!rInfo.fillPropertyMembersByHandle(nullptr, &nAttributes, i_handle))
330
        // unknown property
331
0
        throw UnknownPropertyException(OUString::number(i_handle));
332
333
    // no need to check for READONLY-ness of the property. The method is intended to be called internally, which
334
    // implies it might be invoked for properties which are read-only to the instance's clients, but well allowed
335
    // to change their value.
336
337
0
    Any aConverted, aOld;
338
0
    bool bChanged = convertFastPropertyValue(rGuard, aConverted, aOld, i_handle, i_value);
339
0
    if (!bChanged)
340
0
        return;
341
342
    // don't fire vetoable events. This method is called with our mutex locked, so calling into listeners would not be
343
    // a good idea. The caller is responsible for not invoking this for constrained properties.
344
0
    OSL_ENSURE((nAttributes & PropertyAttribute::CONSTRAINED) == 0,
345
0
               "OPropertySetHelper::setDependentFastPropertyValue: not to be used for constrained "
346
0
               "properties!");
347
348
    // actually set the new value
349
0
    try
350
0
    {
351
0
        setFastPropertyValue_NoBroadcast(rGuard, i_handle, aConverted);
352
0
    }
353
0
    catch (const UnknownPropertyException&)
354
0
    {
355
0
        throw; /* allowed to leave */
356
0
    }
357
0
    catch (const PropertyVetoException&)
358
0
    {
359
0
        throw; /* allowed to leave */
360
0
    }
361
0
    catch (const IllegalArgumentException&)
362
0
    {
363
0
        throw; /* allowed to leave */
364
0
    }
365
0
    catch (const WrappedTargetException&)
366
0
    {
367
0
        throw; /* allowed to leave */
368
0
    }
369
0
    catch (const RuntimeException&)
370
0
    {
371
0
        throw; /* allowed to leave */
372
0
    }
373
0
    catch (const Exception&)
374
0
    {
375
        // not allowed to leave this method
376
0
        WrappedTargetException aWrapped;
377
0
        aWrapped.TargetException = ::cppu::getCaughtException();
378
0
        aWrapped.Context = static_cast<XPropertySet*>(this);
379
0
        throw aWrapped;
380
0
    }
381
382
    // remember the handle/values, for the events to be fired later
383
0
    m_handles.push_back(i_handle);
384
0
    m_newValues.push_back(
385
0
        aConverted); // TODO: setFastPropertyValue notifies the unconverted value here ...?
386
0
    m_oldValues.push_back(aOld);
387
0
}
388
389
// XFastPropertySet
390
void OPropertySetHelper::setFastPropertyValue(sal_Int32 nHandle, const Any& rValue)
391
0
{
392
0
    std::unique_lock aGuard(m_aMutex);
393
0
    setFastPropertyValueImpl(aGuard, nHandle, rValue);
394
0
}
395
396
void OPropertySetHelper::setFastPropertyValueImpl(std::unique_lock<std::mutex>& rGuard,
397
                                                  sal_Int32 nHandle, const Any& rValue)
398
0
{
399
0
    OSL_ENSURE(!m_bDisposed, "object is disposed");
400
401
0
    IPropertyArrayHelper& rInfo = getInfoHelper();
402
0
    sal_Int16 nAttributes;
403
0
    if (!rInfo.fillPropertyMembersByHandle(nullptr, &nAttributes, nHandle))
404
0
    {
405
        // unknown property
406
0
        throw UnknownPropertyException(OUString::number(nHandle));
407
0
    }
408
0
    if (nAttributes & PropertyAttribute::READONLY)
409
0
        throw PropertyVetoException();
410
411
0
    Any aConvertedVal;
412
0
    Any aOldVal;
413
414
    // Will the property change?
415
0
    bool bChanged = convertFastPropertyValue(rGuard, aConvertedVal, aOldVal, nHandle, rValue);
416
0
    if (!bChanged)
417
0
        return;
418
419
    // Is it a constrained property?
420
0
    if (nAttributes & PropertyAttribute::CONSTRAINED)
421
0
    {
422
        // In aValue is the converted rValue
423
        // fire a constrained event
424
        // second parameter NULL means constrained
425
0
        fire(rGuard, &nHandle, &rValue, &aOldVal, 1, true);
426
0
    }
427
428
0
    try
429
0
    {
430
        // set the property to the new value
431
0
        setFastPropertyValue_NoBroadcast(rGuard, nHandle, aConvertedVal);
432
0
    }
433
0
    catch (const css::beans::UnknownPropertyException&)
434
0
    {
435
0
        throw; /* allowed to leave */
436
0
    }
437
0
    catch (const css::beans::PropertyVetoException&)
438
0
    {
439
0
        throw; /* allowed to leave */
440
0
    }
441
0
    catch (const css::lang::IllegalArgumentException&)
442
0
    {
443
0
        throw; /* allowed to leave */
444
0
    }
445
0
    catch (const css::lang::WrappedTargetException&)
446
0
    {
447
0
        throw; /* allowed to leave */
448
0
    }
449
0
    catch (const css::uno::RuntimeException&)
450
0
    {
451
0
        throw; /* allowed to leave */
452
0
    }
453
0
    catch (const css::uno::Exception& e)
454
0
    {
455
        // not allowed to leave this method
456
0
        css::lang::WrappedTargetException aWrap;
457
0
        aWrap.Context = static_cast<css::beans::XPropertySet*>(this);
458
0
        aWrap.TargetException <<= e;
459
460
0
        throw aWrap;
461
0
    }
462
463
    // file a change event, if the value changed
464
0
    impl_fireAll(rGuard, &nHandle, &rValue, &aOldVal, 1);
465
0
}
466
467
// XFastPropertySet
468
Any OPropertySetHelper::getFastPropertyValue(sal_Int32 nHandle)
469
0
{
470
0
    IPropertyArrayHelper& rInfo = getInfoHelper();
471
0
    if (!rInfo.fillPropertyMembersByHandle(nullptr, nullptr, nHandle))
472
        // unknown property
473
0
        throw UnknownPropertyException(OUString::number(nHandle));
474
475
0
    Any aRet;
476
0
    std::unique_lock aGuard(m_aMutex);
477
0
    getFastPropertyValue(aGuard, aRet, nHandle);
478
0
    return aRet;
479
0
}
480
481
void OPropertySetHelper::impl_fireAll(std::unique_lock<std::mutex>& rGuard, sal_Int32* i_handles,
482
                                      const Any* i_newValues, const Any* i_oldValues,
483
                                      sal_Int32 i_count)
484
0
{
485
0
    if (m_handles.empty())
486
0
    {
487
0
        fire(rGuard, i_handles, i_newValues, i_oldValues, i_count, false);
488
0
        return;
489
0
    }
490
491
0
    const size_t additionalEvents = m_handles.size();
492
0
    OSL_ENSURE(additionalEvents == m_newValues.size() && additionalEvents == m_oldValues.size(),
493
0
               "OPropertySetHelper::impl_fireAll: inconsistency!");
494
495
0
    std::vector<sal_Int32> allHandles(additionalEvents + i_count);
496
0
    std::copy(m_handles.begin(), m_handles.end(), allHandles.begin());
497
0
    std::copy(i_handles, i_handles + i_count, allHandles.begin() + additionalEvents);
498
499
0
    std::vector<Any> allNewValues(additionalEvents + i_count);
500
0
    std::copy(m_newValues.begin(), m_newValues.end(), allNewValues.begin());
501
0
    std::copy(i_newValues, i_newValues + i_count, allNewValues.begin() + additionalEvents);
502
503
0
    std::vector<Any> allOldValues(additionalEvents + i_count);
504
0
    std::copy(m_oldValues.begin(), m_oldValues.end(), allOldValues.begin());
505
0
    std::copy(i_oldValues, i_oldValues + i_count, allOldValues.begin() + additionalEvents);
506
507
0
    m_handles.clear();
508
0
    m_newValues.clear();
509
0
    m_oldValues.clear();
510
511
0
    fire(rGuard, allHandles.data(), allNewValues.data(), allOldValues.data(),
512
0
         additionalEvents + i_count, false);
513
0
}
514
515
void OPropertySetHelper::fire(std::unique_lock<std::mutex>& rGuard, const sal_Int32* pnHandles,
516
                              const Any* pNewValues, const Any* pOldValues,
517
                              sal_Int32 nHandles, // This is the Count of the array
518
                              bool bVetoable)
519
0
{
520
    // Only fire, if one or more properties changed
521
0
    if (!nHandles)
522
0
        return;
523
524
    // create the event sequence of all changed properties
525
0
    Sequence<PropertyChangeEvent> aEvts(nHandles);
526
0
    PropertyChangeEvent* pEvts = aEvts.getArray();
527
0
    Reference<XInterface> xSource(static_cast<XPropertySet*>(this), UNO_QUERY);
528
0
    sal_Int32 i;
529
0
    sal_Int32 nChangesLen = 0;
530
    // Loop over all changed properties to fill the event struct
531
0
    for (i = 0; i < nHandles; i++)
532
0
    {
533
        // Vetoable fire and constrained attribute set or
534
        // Change fire and Changed and bound attribute set
535
0
        IPropertyArrayHelper& rInfo = getInfoHelper();
536
0
        sal_Int16 nAttributes;
537
0
        OUString aPropName;
538
0
        rInfo.fillPropertyMembersByHandle(&aPropName, &nAttributes, pnHandles[i]);
539
540
0
        if ((bVetoable && (nAttributes & PropertyAttribute::CONSTRAINED))
541
0
            || (!bVetoable && (nAttributes & PropertyAttribute::BOUND)))
542
0
        {
543
0
            pEvts[nChangesLen].Source = xSource;
544
0
            pEvts[nChangesLen].PropertyName = aPropName;
545
0
            pEvts[nChangesLen].PropertyHandle = pnHandles[i];
546
0
            pEvts[nChangesLen].OldValue = pOldValues[i];
547
0
            pEvts[nChangesLen].NewValue = pNewValues[i];
548
0
            nChangesLen++;
549
0
        }
550
0
    }
551
552
0
    bool bIgnoreRuntimeExceptionsWhileFiring = m_bIgnoreRuntimeExceptionsWhileFiring;
553
554
    // fire the events for all changed properties
555
0
    for (i = 0; i < nChangesLen; i++)
556
0
    {
557
0
        if (bVetoable) // fire change Events?
558
0
            fireVetoableChangeListeners(
559
0
                rGuard, aVetoableLC.getContainer(rGuard, pEvts[i].PropertyHandle), pEvts[i]);
560
0
        else
561
            // get the listener container for the property name
562
0
            firePropertyChangeListeners(
563
0
                rGuard, aBoundLC.getContainer(rGuard, pEvts[i].PropertyHandle), pEvts[i]);
564
565
        // broadcast to all listeners with "" property name
566
0
        if (bVetoable)
567
            // fire change Events?
568
0
            fireVetoableChangeListeners(rGuard, &maVetoableChangeListeners, pEvts[i]);
569
0
        else
570
0
            firePropertyChangeListeners(rGuard, &maPropertyChangeListeners, pEvts[i]);
571
0
    }
572
573
    // reduce array to changed properties
574
0
    aEvts.realloc(nChangesLen);
575
576
0
    if (bVetoable)
577
0
        return;
578
579
0
    if (!maPropertiesChangeListeners.getLength(rGuard))
580
0
        return;
581
582
    // Here is a Bug, unbound properties are also fired
583
0
    OInterfaceIteratorHelper4 aIt(rGuard, maPropertiesChangeListeners);
584
0
    rGuard.unlock();
585
0
    while (aIt.hasMoreElements())
586
0
    {
587
0
        XPropertiesChangeListener* pL = aIt.next().get();
588
0
        try
589
0
        {
590
0
            try
591
0
            {
592
                // fire the whole event sequence to the
593
                // XPropertiesChangeListener's
594
0
                pL->propertiesChange(aEvts);
595
0
            }
596
0
            catch (DisposedException& exc)
597
0
            {
598
0
                OSL_ENSURE(exc.Context.is(), "DisposedException without Context!");
599
0
                if (exc.Context == pL)
600
0
                {
601
0
                    rGuard.lock();
602
0
                    aIt.remove(rGuard);
603
0
                    rGuard.unlock();
604
0
                }
605
0
                else
606
0
                    throw;
607
0
            }
608
0
        }
609
0
        catch (RuntimeException& exc)
610
0
        {
611
0
            SAL_INFO("cppuhelper", "caught RuntimeException while firing listeners: " << exc);
612
0
            if (!bIgnoreRuntimeExceptionsWhileFiring)
613
0
                throw;
614
0
        }
615
0
    }
616
0
    rGuard.lock();
617
0
}
618
619
void OPropertySetHelper::fireVetoableChangeListeners(
620
    std::unique_lock<std::mutex>& rGuard,
621
    comphelper::OInterfaceContainerHelper4<css::beans::XVetoableChangeListener>* pListeners,
622
    const css::beans::PropertyChangeEvent& rChangeEvent)
623
0
{
624
0
    if (!pListeners || !pListeners->getLength(rGuard))
625
0
        return;
626
    // Iterate over all listeners and send events
627
0
    OInterfaceIteratorHelper4 aIt(rGuard, *pListeners);
628
0
    rGuard.unlock();
629
0
    while (aIt.hasMoreElements())
630
0
    {
631
0
        XVetoableChangeListener* pL = aIt.next().get();
632
0
        try
633
0
        {
634
0
            try
635
0
            {
636
0
                pL->vetoableChange(rChangeEvent);
637
0
            }
638
0
            catch (DisposedException& exc)
639
0
            {
640
0
                OSL_ENSURE(exc.Context.is(), "DisposedException without Context!");
641
0
                if (exc.Context == pL)
642
0
                {
643
0
                    rGuard.lock();
644
0
                    aIt.remove(rGuard);
645
0
                    rGuard.unlock();
646
0
                }
647
0
                else
648
0
                    throw;
649
0
            }
650
0
        }
651
0
        catch (RuntimeException& exc)
652
0
        {
653
0
            SAL_INFO("cppuhelper", "caught RuntimeException while firing listeners: " << exc);
654
0
            if (!m_bIgnoreRuntimeExceptionsWhileFiring)
655
0
                throw;
656
0
        }
657
0
    }
658
0
    rGuard.lock();
659
0
}
660
661
void OPropertySetHelper::firePropertyChangeListeners(
662
    std::unique_lock<std::mutex>& rGuard,
663
    comphelper::OInterfaceContainerHelper4<css::beans::XPropertyChangeListener>* pListeners,
664
    const css::beans::PropertyChangeEvent& rChangeEvent)
665
0
{
666
0
    if (!pListeners || !pListeners->getLength(rGuard))
667
0
        return;
668
    // Iterate over all listeners and send events
669
0
    OInterfaceIteratorHelper4 aIt(rGuard, *pListeners);
670
0
    rGuard.unlock();
671
0
    while (aIt.hasMoreElements())
672
0
    {
673
0
        XPropertyChangeListener* pL = aIt.next().get();
674
0
        try
675
0
        {
676
0
            try
677
0
            {
678
0
                pL->propertyChange(rChangeEvent);
679
0
            }
680
0
            catch (DisposedException& exc)
681
0
            {
682
0
                OSL_ENSURE(exc.Context.is(), "DisposedException without Context!");
683
0
                if (exc.Context == pL)
684
0
                {
685
0
                    rGuard.lock();
686
0
                    aIt.remove(rGuard);
687
0
                    rGuard.unlock();
688
0
                }
689
0
                else
690
0
                    throw;
691
0
            }
692
0
        }
693
0
        catch (RuntimeException& exc)
694
0
        {
695
0
            SAL_INFO("cppuhelper", "caught RuntimeException while firing listeners: " << exc);
696
0
            if (!m_bIgnoreRuntimeExceptionsWhileFiring)
697
0
                throw;
698
0
        }
699
0
    }
700
0
    rGuard.lock();
701
0
}
702
703
// OPropertySetHelper
704
void OPropertySetHelper::setFastPropertyValues(std::unique_lock<std::mutex>& rGuard,
705
                                               sal_Int32 nSeqLen, sal_Int32* pHandles,
706
                                               const Any* pValues, sal_Int32 nHitCount)
707
0
{
708
0
    OSL_ENSURE(!m_bDisposed, "object is disposed");
709
710
    // get the map table
711
0
    IPropertyArrayHelper& rPH = getInfoHelper();
712
713
0
    std::unique_ptr<Any[]> pConvertedValues(new Any[nHitCount]);
714
0
    std::unique_ptr<Any[]> pOldValues(new Any[nHitCount]);
715
0
    sal_Int32 n = 0;
716
0
    sal_Int32 i;
717
718
0
    for (i = 0; i < nSeqLen; i++)
719
0
    {
720
0
        if (pHandles[i] != -1)
721
0
        {
722
0
            sal_Int16 nAttributes;
723
0
            rPH.fillPropertyMembersByHandle(nullptr, &nAttributes, pHandles[i]);
724
0
            if (nAttributes & PropertyAttribute::READONLY)
725
0
                throw PropertyVetoException();
726
            // Will the property change?
727
0
            if (convertFastPropertyValue(rGuard, pConvertedValues[n], pOldValues[n], pHandles[i],
728
0
                                         pValues[i]))
729
0
            {
730
                // only increment if the property really change
731
0
                pHandles[n] = pHandles[i];
732
0
                n++;
733
0
            }
734
0
        }
735
0
    }
736
737
    // fire vetoable events
738
0
    fire(rGuard, pHandles, pConvertedValues.get(), pOldValues.get(), n, true);
739
740
    // Loop over all changed properties
741
0
    for (i = 0; i < n; i++)
742
0
    {
743
        // Will the property change?
744
0
        setFastPropertyValue_NoBroadcast(rGuard, pHandles[i], pConvertedValues[i]);
745
0
    }
746
747
    // fire change events
748
0
    impl_fireAll(rGuard, pHandles, pConvertedValues.get(), pOldValues.get(), n);
749
0
}
750
751
// XMultiPropertySet
752
/**
753
 * The sequence may be contain not known properties. The implementation
754
 * must ignore these properties.
755
 */
756
void OPropertySetHelper::setPropertyValues(const Sequence<OUString>& rPropertyNames,
757
                                           const Sequence<Any>& rValues)
758
0
{
759
0
    sal_Int32 nSeqLen = rPropertyNames.getLength();
760
0
    if (nSeqLen != rValues.getLength())
761
0
        throw IllegalArgumentException(u"lengths do not match"_ustr,
762
0
                                       static_cast<XPropertySet*>(this), -1);
763
0
    std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[nSeqLen]);
764
    // get the map table
765
0
    IPropertyArrayHelper& rPH = getInfoHelper();
766
    // fill the handle array
767
0
    sal_Int32 nHitCount = rPH.fillHandles(pHandles.get(), rPropertyNames);
768
0
    if (nHitCount == 0)
769
0
        return;
770
0
    std::unique_lock aGuard(m_aMutex);
771
0
    setFastPropertyValues(aGuard, nSeqLen, pHandles.get(), rValues.getConstArray(), nHitCount);
772
0
}
773
774
// XMultiPropertySet
775
Sequence<Any> OPropertySetHelper::getPropertyValues(const Sequence<OUString>& rPropertyNames)
776
0
{
777
0
    sal_Int32 nSeqLen = rPropertyNames.getLength();
778
0
    std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[nSeqLen]);
779
0
    Sequence<Any> aValues(nSeqLen);
780
781
    // get the map table
782
0
    IPropertyArrayHelper& rPH = getInfoHelper();
783
    // fill the handle array
784
0
    rPH.fillHandles(pHandles.get(), rPropertyNames);
785
786
0
    Any* pValues = aValues.getArray();
787
788
0
    std::unique_lock aGuard(m_aMutex);
789
    // fill the sequence with the values
790
0
    for (sal_Int32 i = 0; i < nSeqLen; i++)
791
0
        getFastPropertyValue(aGuard, pValues[i], pHandles[i]);
792
793
0
    return aValues;
794
0
}
795
796
// XMultiPropertySet
797
void OPropertySetHelper::addPropertiesChangeListener(
798
    const Sequence<OUString>&, const Reference<XPropertiesChangeListener>& rListener)
799
55
{
800
55
    std::unique_lock g(m_aMutex);
801
55
    maPropertiesChangeListeners.addInterface(g, rListener);
802
55
}
803
804
// XMultiPropertySet
805
void OPropertySetHelper::removePropertiesChangeListener(
806
    const Reference<XPropertiesChangeListener>& rListener)
807
55
{
808
55
    std::unique_lock g(m_aMutex);
809
55
    maPropertiesChangeListeners.removeInterface(g, rListener);
810
55
}
811
812
// XMultiPropertySet
813
void OPropertySetHelper::firePropertiesChangeEvent(
814
    const Sequence<OUString>& rPropertyNames, const Reference<XPropertiesChangeListener>& rListener)
815
0
{
816
0
    sal_Int32 nLen = rPropertyNames.getLength();
817
0
    std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[nLen]);
818
0
    IPropertyArrayHelper& rPH = getInfoHelper();
819
0
    rPH.fillHandles(pHandles.get(), rPropertyNames);
820
821
    // get the count of matching properties
822
0
    sal_Int32 nFireLen = 0;
823
0
    sal_Int32 i;
824
0
    for (i = 0; i < nLen; i++)
825
0
        if (pHandles[i] != -1)
826
0
            nFireLen++;
827
828
0
    Sequence<PropertyChangeEvent> aChanges(nFireLen);
829
0
    PropertyChangeEvent* pChanges = aChanges.getArray();
830
831
0
    {
832
        // must lock the mutex outside the loop. So all values are consistent.
833
0
        std::unique_lock aGuard(m_aMutex);
834
0
        Reference<XInterface> xSource(static_cast<XPropertySet*>(this), UNO_QUERY);
835
0
        sal_Int32 nFirePos = 0;
836
0
        for (i = 0; i < nLen; i++)
837
0
        {
838
0
            if (pHandles[i] != -1)
839
0
            {
840
0
                pChanges[nFirePos].Source = xSource;
841
0
                pChanges[nFirePos].PropertyName = rPropertyNames[i];
842
0
                pChanges[nFirePos].PropertyHandle = pHandles[i];
843
0
                getFastPropertyValue(aGuard, pChanges[nFirePos].OldValue, pHandles[i]);
844
0
                pChanges[nFirePos].NewValue = pChanges[nFirePos].OldValue;
845
0
                nFirePos++;
846
0
            }
847
0
        }
848
        // release guard to fire events
849
0
    }
850
0
    if (nFireLen)
851
0
        rListener->propertiesChange(aChanges);
852
0
}
853
854
399k
UnoImplBase::~UnoImplBase() {}
855
856
} // end namespace comphelper
857
858
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */