Avoid potential negative array index access to cached text.
[LibreOffice.git] / cppuhelper / source / propertysetmixin.cxx
blob50a8be1f84367523a4a490056d6621145a328a70
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <algorithm>
23 #include <cassert>
24 #include <map>
25 #include <mutex>
26 #include <set>
27 #include <utility>
28 #include <vector>
30 #include <com/sun/star/beans/Property.hpp>
31 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
32 #include <com/sun/star/beans/PropertyAttribute.hpp>
33 #include <com/sun/star/beans/PropertyValue.hpp>
34 #include <com/sun/star/beans/PropertyVetoException.hpp>
35 #include <com/sun/star/beans/UnknownPropertyException.hpp>
36 #include <com/sun/star/beans/XFastPropertySet.hpp>
37 #include <com/sun/star/beans/XPropertyAccess.hpp>
38 #include <com/sun/star/beans/XPropertyChangeListener.hpp>
39 #include <com/sun/star/beans/XPropertySet.hpp>
40 #include <com/sun/star/beans/XPropertySetInfo.hpp>
41 #include <com/sun/star/beans/XVetoableChangeListener.hpp>
42 #include <com/sun/star/container/NoSuchElementException.hpp>
43 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
44 #include <com/sun/star/lang/DisposedException.hpp>
45 #include <com/sun/star/lang/EventObject.hpp>
46 #include <com/sun/star/lang/IllegalAccessException.hpp>
47 #include <com/sun/star/lang/IllegalArgumentException.hpp>
48 #include <com/sun/star/lang/WrappedTargetException.hpp>
49 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
50 #include <com/sun/star/reflection/XCompoundTypeDescription.hpp>
51 #include <com/sun/star/reflection/XIdlClass.hpp>
52 #include <com/sun/star/reflection/XIdlField2.hpp>
53 #include <com/sun/star/reflection/XIndirectTypeDescription.hpp>
54 #include <com/sun/star/reflection/XInterfaceAttributeTypeDescription2.hpp>
55 #include <com/sun/star/reflection/XInterfaceMemberTypeDescription.hpp>
56 #include <com/sun/star/reflection/XInterfaceTypeDescription2.hpp>
57 #include <com/sun/star/reflection/XStructTypeDescription.hpp>
58 #include <com/sun/star/reflection/XTypeDescription.hpp>
59 #include <com/sun/star/reflection/theCoreReflection.hpp>
60 #include <com/sun/star/uno/Any.hxx>
61 #include <com/sun/star/uno/Reference.hxx>
62 #include <com/sun/star/uno/RuntimeException.hpp>
63 #include <com/sun/star/uno/Sequence.hxx>
64 #include <com/sun/star/uno/Type.hxx>
65 #include <com/sun/star/uno/TypeClass.hpp>
66 #include <com/sun/star/uno/XComponentContext.hpp>
67 #include <com/sun/star/uno/XInterface.hpp>
68 #include <cppu/unotype.hxx>
69 #include <cppuhelper/exc_hlp.hxx>
70 #include <cppuhelper/implbase.hxx>
71 #include <cppuhelper/propertysetmixin.hxx>
72 #include <cppuhelper/weak.hxx>
73 #include <rtl/ref.hxx>
74 #include <rtl/ustring.hxx>
75 #include <sal/types.h>
76 #include <salhelper/simplereferenceobject.hxx>
78 using cppu::PropertySetMixinImpl;
80 namespace {
82 struct PropertyData {
83 explicit PropertyData(
84 css::beans::Property theProperty, bool thePresent):
85 property(std::move(theProperty)), present(thePresent) {}
87 css::beans::Property property;
88 bool present;
91 struct Data: public salhelper::SimpleReferenceObject {
92 typedef std::map< OUString, PropertyData > PropertyMap;
94 PropertyMap properties;
96 PropertyMap::const_iterator get(
97 css::uno::Reference< css::uno::XInterface > const & object,
98 OUString const & name) const;
100 protected:
101 void initProperties(
102 css::uno::Reference< css::reflection::XTypeDescription > const & type,
103 css::uno::Sequence< OUString > const & absentOptional,
104 std::vector< OUString > * handleNames)
106 std::set<OUString> seen;
107 initProperties(type, absentOptional, handleNames, &seen);
110 private:
111 void initProperties(
112 css::uno::Reference< css::reflection::XTypeDescription > const & type,
113 css::uno::Sequence< OUString > const & absentOptional,
114 std::vector< OUString > * handleNames, std::set<OUString> * seen);
116 static css::uno::Reference< css::reflection::XTypeDescription >
117 resolveTypedefs(
118 css::uno::Reference< css::reflection::XTypeDescription > const & type);
121 Data::PropertyMap::const_iterator Data::get(
122 css::uno::Reference< css::uno::XInterface > const & object,
123 OUString const & name) const
125 PropertyMap::const_iterator i(properties.find(name));
126 if (i == properties.end() || !i->second.present) {
127 throw css::beans::UnknownPropertyException(name, object);
129 return i;
132 void Data::initProperties(
133 css::uno::Reference< css::reflection::XTypeDescription > const & type,
134 css::uno::Sequence< OUString > const & absentOptional,
135 std::vector< OUString > * handleNames, std::set<OUString> * seen)
137 css::uno::Reference< css::reflection::XInterfaceTypeDescription2 > ifc(
138 resolveTypedefs(type), css::uno::UNO_QUERY_THROW);
139 if (!seen->insert(ifc->getName()).second)
140 return;
142 const css::uno::Sequence<
143 css::uno::Reference< css::reflection::XTypeDescription > > bases(
144 ifc->getBaseTypes());
145 for (const auto & i : bases) {
146 initProperties(i, absentOptional, handleNames, seen);
148 const css::uno::Sequence<
149 css::uno::Reference<
150 css::reflection::XInterfaceMemberTypeDescription > > members(
151 ifc->getMembers());
152 OUString const * absentBegin = absentOptional.getConstArray();
153 OUString const * absentEnd =
154 absentBegin + absentOptional.getLength();
155 for (const auto & m : members) {
156 if (m->getTypeClass()
157 == css::uno::TypeClass_INTERFACE_ATTRIBUTE)
159 css::uno::Reference<
160 css::reflection::XInterfaceAttributeTypeDescription2 > attr(
161 m, css::uno::UNO_QUERY_THROW);
162 sal_Int16 attrAttribs = 0;
163 if (attr->isBound()) {
164 attrAttribs |= css::beans::PropertyAttribute::BOUND;
166 bool bSetUnknown = false;
167 if (attr->isReadOnly()) {
168 attrAttribs |= css::beans::PropertyAttribute::READONLY;
169 bSetUnknown = true;
171 css::uno::Sequence<
172 css::uno::Reference<
173 css::reflection::XCompoundTypeDescription > > excs(
174 attr->getGetExceptions());
175 bool bGetUnknown = false;
176 //XXX Special interpretation of getter/setter exceptions only
177 // works if the specified exceptions are of the exact type, not
178 // of a supertype:
179 for (const auto & ex : std::as_const(excs)) {
180 if ( ex->getName() == "com.sun.star.beans.UnknownPropertyException" )
182 bGetUnknown = true;
183 break;
186 excs = attr->getSetExceptions();
187 for (const auto & ex : std::as_const(excs)) {
188 if ( ex->getName() == "com.sun.star.beans.UnknownPropertyException" )
190 bSetUnknown = true;
191 } else if ( ex->getName() == "com.sun.star.beans.PropertyVetoException" )
193 attrAttribs
194 |= css::beans::PropertyAttribute::CONSTRAINED;
197 if (bGetUnknown && bSetUnknown) {
198 attrAttribs |= css::beans::PropertyAttribute::OPTIONAL;
200 css::uno::Reference< css::reflection::XTypeDescription > t(
201 attr->getType());
202 for (;;)
204 t = resolveTypedefs(t);
205 sal_Int16 n;
206 if (t->getName().startsWith(
207 "com.sun.star.beans.Ambiguous<"))
209 n = css::beans::PropertyAttribute::MAYBEAMBIGUOUS;
210 } else if (t->getName().startsWith(
211 "com.sun.star.beans.Defaulted<"))
213 n = css::beans::PropertyAttribute::MAYBEDEFAULT;
214 } else if (t->getName().startsWith(
215 "com.sun.star.beans.Optional<"))
217 n = css::beans::PropertyAttribute::MAYBEVOID;
218 } else {
219 break;
221 if ((attrAttribs & n) != 0) {
222 break;
224 attrAttribs |= n;
225 const css::uno::Sequence<
226 css::uno::Reference< css::reflection::XTypeDescription > >
227 args(
228 css::uno::Reference<
229 css::reflection::XStructTypeDescription >(
230 t, css::uno::UNO_QUERY_THROW)->
231 getTypeArguments());
232 if (args.getLength() != 1) {
233 throw css::uno::RuntimeException(
234 "inconsistent UNO type registry");
236 t = args[0];
238 std::vector< OUString >::size_type handles
239 = handleNames->size();
240 if (handles > SAL_MAX_INT32) {
241 throw css::uno::RuntimeException(
242 "interface type has too many attributes");
244 OUString name(m->getMemberName());
245 if (!properties.emplace(
246 name,
247 PropertyData(
248 css::beans::Property(
249 name, static_cast< sal_Int32 >(handles),
250 css::uno::Type(
251 t->getTypeClass(), t->getName()),
252 attrAttribs),
253 (std::find(absentBegin, absentEnd, name)
254 == absentEnd))).
255 second)
257 throw css::uno::RuntimeException(
258 "inconsistent UNO type registry");
260 handleNames->push_back(name);
265 css::uno::Reference< css::reflection::XTypeDescription > Data::resolveTypedefs(
266 css::uno::Reference< css::reflection::XTypeDescription > const & type)
268 css::uno::Reference< css::reflection::XTypeDescription > t(type);
269 while (t->getTypeClass() == css::uno::TypeClass_TYPEDEF) {
270 t = css::uno::Reference< css::reflection::XIndirectTypeDescription >(
271 t, css::uno::UNO_QUERY_THROW)->getReferencedType();
273 return t;
276 class Info: public cppu::WeakImplHelper< css::beans::XPropertySetInfo > {
277 public:
278 explicit Info(Data * data): m_data(data) {}
280 virtual css::uno::Sequence< css::beans::Property > SAL_CALL getProperties() override;
282 virtual css::beans::Property SAL_CALL getPropertyByName(
283 OUString const & name) override;
285 virtual sal_Bool SAL_CALL hasPropertyByName(OUString const & name) override;
287 private:
288 rtl::Reference< Data > m_data;
291 css::uno::Sequence< css::beans::Property > Info::getProperties()
293 assert(m_data->properties.size() <= SAL_MAX_INT32);
294 css::uno::Sequence< css::beans::Property > s(
295 static_cast< sal_Int32 >(m_data->properties.size()));
296 auto r = asNonConstRange(s);
297 sal_Int32 n = 0;
298 for (const auto& rEntry : m_data->properties)
300 if (rEntry.second.present) {
301 r[n++] = rEntry.second.property;
304 s.realloc(n);
305 return s;
308 css::beans::Property Info::getPropertyByName(OUString const & name)
310 return m_data->get(static_cast< cppu::OWeakObject * >(this), name)->
311 second.property;
314 sal_Bool Info::hasPropertyByName(OUString const & name)
316 Data::PropertyMap::iterator i(m_data->properties.find(name));
317 return i != m_data->properties.end() && i->second.present;
320 typedef
321 std::multiset< css::uno::Reference< css::beans::XPropertyChangeListener > >
322 BoundListenerBag;
326 class PropertySetMixinImpl::BoundListeners::Impl {
327 public:
328 BoundListenerBag specificListeners;
329 BoundListenerBag unspecificListeners;
330 css::beans::PropertyChangeEvent event;
333 PropertySetMixinImpl::BoundListeners::BoundListeners(): m_impl(new Impl) {}
335 PropertySetMixinImpl::BoundListeners::~BoundListeners() {
336 delete m_impl;
339 void PropertySetMixinImpl::BoundListeners::notify() const {
340 for (const auto& rxListener : m_impl->specificListeners)
342 try {
343 rxListener->propertyChange(m_impl->event);
344 } catch (css::lang::DisposedException &) {}
346 for (const auto& rxListener : m_impl->unspecificListeners)
348 try {
349 rxListener->propertyChange(m_impl->event);
350 } catch (css::lang::DisposedException &) {}
354 class PropertySetMixinImpl::Impl: public Data {
355 public:
356 Impl(
357 css::uno::Reference< css::uno::XComponentContext > const & context,
358 Implements theImplements,
359 css::uno::Sequence< OUString > const & absentOptional,
360 css::uno::Type const & type);
362 OUString const & translateHandle(
363 css::uno::Reference< css::uno::XInterface > const & object,
364 sal_Int32 handle) const;
366 void setProperty(
367 css::uno::Reference< css::uno::XInterface > const & object,
368 OUString const & name, css::uno::Any const & value,
369 bool isAmbiguous, bool isDefaulted, sal_Int16 illegalArgumentPosition)
370 const;
372 css::uno::Any getProperty(
373 css::uno::Reference< css::uno::XInterface > const & object,
374 OUString const & name, css::beans::PropertyState * state) const;
376 PropertySetMixinImpl::Implements implements;
377 css::uno::Sequence< OUString > handleMap;
379 typedef std::map< OUString, BoundListenerBag > BoundListenerMap;
381 typedef
382 std::multiset< css::uno::Reference< css::beans::XVetoableChangeListener > >
383 VetoListenerBag;
385 typedef std::map< OUString, VetoListenerBag > VetoListenerMap;
387 mutable std::mutex mutex;
388 BoundListenerMap boundListeners;
389 VetoListenerMap vetoListeners;
390 bool disposed;
392 private:
393 css::uno::Reference< css::reflection::XIdlClass > getReflection(
394 OUString const & typeName) const;
396 static css::uno::Any wrapValue(
397 css::uno::Reference< css::uno::XInterface > const & object,
398 css::uno::Any const & value,
399 css::uno::Reference< css::reflection::XIdlClass > const & type,
400 bool wrapAmbiguous, bool isAmbiguous, bool wrapDefaulted,
401 bool isDefaulted, bool wrapOptional);
403 css::uno::Reference< css::uno::XComponentContext > const & m_context;
404 css::uno::Type m_type;
405 css::uno::Reference< css::reflection::XIdlClass > m_idlClass;
408 PropertySetMixinImpl::Impl::Impl(
409 css::uno::Reference< css::uno::XComponentContext > const & context,
410 Implements theImplements,
411 css::uno::Sequence< OUString > const & absentOptional,
412 css::uno::Type const & type):
413 implements(theImplements), disposed(false), m_context(context),
414 m_type(type)
416 assert(context.is());
417 assert(
418 (implements
419 & ~(IMPLEMENTS_PROPERTY_SET | IMPLEMENTS_FAST_PROPERTY_SET
420 | IMPLEMENTS_PROPERTY_ACCESS))
421 == 0);
422 m_idlClass = getReflection(m_type.getTypeName());
423 css::uno::Reference< css::reflection::XTypeDescription > ifc;
424 try {
425 ifc.set(
426 css::uno::Reference< css::container::XHierarchicalNameAccess >(
427 m_context->getValueByName(
428 "/singletons/com.sun.star.reflection."
429 "theTypeDescriptionManager"),
430 css::uno::UNO_QUERY_THROW)->getByHierarchicalName(
431 m_type.getTypeName()),
432 css::uno::UNO_QUERY_THROW);
433 } catch (css::container::NoSuchElementException & e) {
434 css::uno::Any anyEx = cppu::getCaughtException();
435 throw css::lang::WrappedTargetRuntimeException(
436 "unexpected com.sun.star.container.NoSuchElementException: "
437 + e.Message,
438 nullptr, anyEx );
440 std::vector< OUString > handleNames;
441 initProperties(ifc, absentOptional, &handleNames);
442 std::vector< OUString >::size_type size = handleNames.size();
443 assert(size <= SAL_MAX_INT32);
444 handleMap.realloc(static_cast< sal_Int32 >(size));
445 std::copy(handleNames.begin(), handleNames.end(), handleMap.getArray());
448 OUString const & PropertySetMixinImpl::Impl::translateHandle(
449 css::uno::Reference< css::uno::XInterface > const & object,
450 sal_Int32 handle) const
452 if (handle < 0 || handle >= handleMap.getLength()) {
453 throw css::beans::UnknownPropertyException(
454 "bad handle " + OUString::number(handle), object);
456 return handleMap[handle];
459 void PropertySetMixinImpl::Impl::setProperty(
460 css::uno::Reference< css::uno::XInterface > const & object,
461 OUString const & name, css::uno::Any const & value, bool isAmbiguous,
462 bool isDefaulted, sal_Int16 illegalArgumentPosition) const
464 PropertyMap::const_iterator i(properties.find(name));
465 if (i == properties.end()) {
466 throw css::beans::UnknownPropertyException(name, object);
468 if ((isAmbiguous
469 && ((i->second.property.Attributes
470 & css::beans::PropertyAttribute::MAYBEAMBIGUOUS)
471 == 0))
472 || (isDefaulted
473 && ((i->second.property.Attributes
474 & css::beans::PropertyAttribute::MAYBEDEFAULT)
475 == 0)))
477 throw css::lang::IllegalArgumentException(
478 ("flagging as ambiguous/defaulted non-ambiguous/defaulted property "
479 + name),
480 object, illegalArgumentPosition);
482 css::uno::Reference< css::reflection::XIdlField2 > f(
483 m_idlClass->getField(name), css::uno::UNO_QUERY_THROW);
484 css::uno::Any o(object->queryInterface(m_type));
485 css::uno::Any v(
486 wrapValue(
487 object, value,
488 (css::uno::Reference< css::reflection::XIdlField2 >(
489 m_idlClass->getField(name), css::uno::UNO_QUERY_THROW)->
490 getType()),
491 ((i->second.property.Attributes
492 & css::beans::PropertyAttribute::MAYBEAMBIGUOUS)
493 != 0),
494 isAmbiguous,
495 ((i->second.property.Attributes
496 & css::beans::PropertyAttribute::MAYBEDEFAULT)
497 != 0),
498 isDefaulted,
499 ((i->second.property.Attributes
500 & css::beans::PropertyAttribute::MAYBEVOID)
501 != 0)));
502 try {
503 f->set(o, v);
504 } catch (css::lang::IllegalArgumentException & e) {
505 if (e.ArgumentPosition == 1) {
506 throw css::lang::IllegalArgumentException(
507 e.Message, object, illegalArgumentPosition);
508 } else {
509 css::uno::Any anyEx = cppu::getCaughtException();
510 throw css::lang::WrappedTargetRuntimeException(
511 "unexpected com.sun.star.lang.IllegalArgumentException: "
512 + e.Message,
513 object, anyEx );
515 } catch (css::lang::IllegalAccessException &) {
516 //TODO Clarify whether PropertyVetoException is the correct exception
517 // to throw when trying to set a read-only property:
518 throw css::beans::PropertyVetoException(
519 "cannot set read-only property " + name, object);
520 } catch (css::lang::WrappedTargetRuntimeException & e) {
521 //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not
522 // guaranteed to originate directly within XIdlField2.get (and thus have
523 // the expected semantics); it might also be passed through from lower
524 // layers.
525 if (e.TargetException.isExtractableTo(
526 cppu::UnoType<css::beans::UnknownPropertyException>::get())
527 && ((i->second.property.Attributes
528 & css::beans::PropertyAttribute::OPTIONAL)
529 != 0))
531 throw css::beans::UnknownPropertyException(name, object);
532 } else if (e.TargetException.isExtractableTo(
533 cppu::UnoType<css::beans::PropertyVetoException>::get())
534 && ((i->second.property.Attributes
535 & css::beans::PropertyAttribute::CONSTRAINED)
536 != 0))
538 css::beans::PropertyVetoException exc;
539 e.TargetException >>= exc;
540 if (exc.Message.isEmpty() )
541 throw css::beans::PropertyVetoException("Invalid " + name, object);
542 else
543 throw exc;
544 } else {
545 throw css::lang::WrappedTargetException(
546 e.Message, object, e.TargetException);
551 css::uno::Any PropertySetMixinImpl::Impl::getProperty(
552 css::uno::Reference< css::uno::XInterface > const & object,
553 OUString const & name, css::beans::PropertyState * state) const
555 PropertyMap::const_iterator i(properties.find(name));
556 if (i == properties.end()) {
557 throw css::beans::UnknownPropertyException(name, object);
559 css::uno::Reference< css::reflection::XIdlField2 > field(
560 m_idlClass->getField(name), css::uno::UNO_QUERY_THROW);
561 css::uno::Any value;
562 try {
563 value = field->get(object->queryInterface(m_type));
564 } catch (css::lang::IllegalArgumentException & e) {
565 css::uno::Any anyEx = cppu::getCaughtException();
566 throw css::lang::WrappedTargetRuntimeException(
567 "unexpected com.sun.star.lang.IllegalArgumentException: "
568 + e.Message,
569 object, anyEx );
570 } catch (css::lang::WrappedTargetRuntimeException & e) {
571 //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not
572 // guaranteed to originate directly within XIdlField2.get (and thus have
573 // the expected semantics); it might also be passed through from lower
574 // layers.
575 if (e.TargetException.isExtractableTo(
576 cppu::UnoType<css::beans::UnknownPropertyException>::get())
577 && ((i->second.property.Attributes
578 & css::beans::PropertyAttribute::OPTIONAL)
579 != 0))
581 throw css::beans::UnknownPropertyException(name, object);
582 } else {
583 throw css::lang::WrappedTargetException(
584 e.Message, object, e.TargetException);
587 bool undoAmbiguous
588 = ((i->second.property.Attributes
589 & css::beans::PropertyAttribute::MAYBEAMBIGUOUS)
590 != 0);
591 bool undoDefaulted
592 = ((i->second.property.Attributes
593 & css::beans::PropertyAttribute::MAYBEDEFAULT)
594 != 0);
595 bool undoOptional
596 = ((i->second.property.Attributes
597 & css::beans::PropertyAttribute::MAYBEVOID)
598 != 0);
599 bool isAmbiguous = false;
600 bool isDefaulted = false;
601 while (undoAmbiguous || undoDefaulted || undoOptional) {
602 if (undoAmbiguous
603 && value.getValueTypeName().startsWith(
604 "com.sun.star.beans.Ambiguous<"))
606 css::uno::Reference< css::reflection::XIdlClass > ambiguous(
607 getReflection(value.getValueTypeName()));
608 try {
609 if (!(css::uno::Reference< css::reflection::XIdlField2 >(
610 ambiguous->getField("IsAmbiguous"),
611 css::uno::UNO_QUERY_THROW)->get(value)
612 >>= isAmbiguous))
614 throw css::uno::RuntimeException(
615 ("unexpected type of com.sun.star.beans.Ambiguous"
616 " IsAmbiguous member"),
617 object);
619 value = css::uno::Reference< css::reflection::XIdlField2 >(
620 ambiguous->getField("Value"), css::uno::UNO_QUERY_THROW)->
621 get(value);
622 } catch (css::lang::IllegalArgumentException & e) {
623 css::uno::Any anyEx = cppu::getCaughtException();
624 throw css::lang::WrappedTargetRuntimeException(
625 "unexpected com.sun.star.lang.IllegalArgumentException: "
626 + e.Message,
627 object, anyEx );
629 undoAmbiguous = false;
630 } else if (undoDefaulted
631 && value.getValueTypeName().startsWith(
632 "com.sun.star.beans.Defaulted<"))
634 css::uno::Reference< css::reflection::XIdlClass > defaulted(
635 getReflection(value.getValueTypeName()));
636 try {
638 if (!(css::uno::Reference< css::reflection::XIdlField2 >(
639 defaulted->getField("IsDefaulted"),
640 css::uno::UNO_QUERY_THROW)->get(value)
641 >>= isDefaulted))
643 throw css::uno::RuntimeException(
644 ("unexpected type of com.sun.star.beans.Defaulted"
645 " IsDefaulted member"),
646 object);
648 value = css::uno::Reference< css::reflection::XIdlField2 >(
649 defaulted->getField("Value"), css::uno::UNO_QUERY_THROW)->
650 get(value);
651 } catch (css::lang::IllegalArgumentException & e) {
652 css::uno::Any anyEx = cppu::getCaughtException();
653 throw css::lang::WrappedTargetRuntimeException(
654 "unexpected com.sun.star.lang.IllegalArgumentException: "
655 + e.Message,
656 object, anyEx );
658 undoDefaulted = false;
659 } else if (undoOptional
660 && value.getValueTypeName().startsWith(
661 "com.sun.star.beans.Optional<"))
663 css::uno::Reference< css::reflection::XIdlClass > optional(
664 getReflection(value.getValueTypeName()));
665 try {
666 bool present = false;
667 if (!(css::uno::Reference< css::reflection::XIdlField2 >(
668 optional->getField("IsPresent"),
669 css::uno::UNO_QUERY_THROW)->get(value)
670 >>= present))
672 throw css::uno::RuntimeException(
673 ("unexpected type of com.sun.star.beans.Optional"
674 " IsPresent member"),
675 object);
677 if (!present) {
678 value.clear();
679 break;
681 value = css::uno::Reference< css::reflection::XIdlField2 >(
682 optional->getField("Value"), css::uno::UNO_QUERY_THROW)->
683 get(value);
684 } catch (css::lang::IllegalArgumentException & e) {
685 css::uno::Any anyEx = cppu::getCaughtException();
686 throw css::lang::WrappedTargetRuntimeException(
687 "unexpected com.sun.star.lang.IllegalArgumentException: "
688 + e.Message,
689 object, anyEx );
691 undoOptional = false;
692 } else {
693 throw css::uno::RuntimeException(
694 "unexpected type of attribute " + name, object);
697 if (state != nullptr) {
698 //XXX If isAmbiguous && isDefaulted, arbitrarily choose AMBIGUOUS_VALUE
699 // over DEFAULT_VALUE:
700 *state = isAmbiguous
701 ? css::beans::PropertyState_AMBIGUOUS_VALUE
702 : isDefaulted
703 ? css::beans::PropertyState_DEFAULT_VALUE
704 : css::beans::PropertyState_DIRECT_VALUE;
706 return value;
709 css::uno::Reference< css::reflection::XIdlClass >
710 PropertySetMixinImpl::Impl::getReflection(OUString const & typeName) const
712 return css::uno::Reference< css::reflection::XIdlClass >(
713 css::reflection::theCoreReflection::get(m_context)->forName(typeName),
714 css::uno::UNO_SET_THROW);
717 css::uno::Any PropertySetMixinImpl::Impl::wrapValue(
718 css::uno::Reference< css::uno::XInterface > const & object,
719 css::uno::Any const & value,
720 css::uno::Reference< css::reflection::XIdlClass > const & type,
721 bool wrapAmbiguous, bool isAmbiguous, bool wrapDefaulted, bool isDefaulted,
722 bool wrapOptional)
724 assert(wrapAmbiguous || !isAmbiguous);
725 assert(wrapDefaulted || !isDefaulted);
726 if (wrapAmbiguous
727 && type->getName().startsWith("com.sun.star.beans.Ambiguous<"))
729 css::uno::Any strct;
730 type->createObject(strct);
731 try {
732 css::uno::Reference< css::reflection::XIdlField2 > field(
733 type->getField("Value"), css::uno::UNO_QUERY_THROW);
734 field->set(
735 strct,
736 wrapValue(
737 object, value, field->getType(), false, false,
738 wrapDefaulted, isDefaulted, wrapOptional));
739 css::uno::Reference< css::reflection::XIdlField2 >(
740 type->getField("IsAmbiguous"), css::uno::UNO_QUERY_THROW)->set(
741 strct, css::uno::Any(isAmbiguous));
742 } catch (css::lang::IllegalArgumentException & e) {
743 css::uno::Any anyEx = cppu::getCaughtException();
744 throw css::lang::WrappedTargetRuntimeException(
745 "unexpected com.sun.star.lang.IllegalArgumentException: "
746 + e.Message,
747 object, anyEx );
748 } catch (css::lang::IllegalAccessException & e) {
749 css::uno::Any anyEx = cppu::getCaughtException();
750 throw css::lang::WrappedTargetRuntimeException(
751 "unexpected com.sun.star.lang.IllegalAccessException: "
752 + e.Message,
753 object, anyEx );
755 return strct;
757 if (wrapDefaulted
758 && type->getName().startsWith("com.sun.star.beans.Defaulted<"))
760 css::uno::Any strct;
761 type->createObject(strct);
762 try {
763 css::uno::Reference< css::reflection::XIdlField2 > field(
764 type->getField("Value"), css::uno::UNO_QUERY_THROW);
765 field->set(
766 strct,
767 wrapValue(
768 object, value, field->getType(), wrapAmbiguous, isAmbiguous,
769 false, false, wrapOptional));
770 css::uno::Reference< css::reflection::XIdlField2 >(
771 type->getField("IsDefaulted"), css::uno::UNO_QUERY_THROW)->set(
772 strct, css::uno::Any(isDefaulted));
773 } catch (css::lang::IllegalArgumentException & e) {
774 css::uno::Any anyEx = cppu::getCaughtException();
775 throw css::lang::WrappedTargetRuntimeException(
776 "unexpected com.sun.star.lang.IllegalArgumentException: "
777 + e.Message,
778 object, anyEx );
779 } catch (css::lang::IllegalAccessException & e) {
780 css::uno::Any anyEx = cppu::getCaughtException();
781 throw css::lang::WrappedTargetRuntimeException(
782 "unexpected com.sun.star.lang.IllegalAccessException: "
783 + e.Message,
784 object, anyEx );
786 return strct;
788 if (wrapOptional
789 && type->getName().startsWith("com.sun.star.beans.Optional<"))
791 css::uno::Any strct;
792 type->createObject(strct);
793 bool present = value.hasValue();
794 try {
795 css::uno::Reference< css::reflection::XIdlField2 >(
796 type->getField("IsPresent"), css::uno::UNO_QUERY_THROW)->set(
797 strct, css::uno::Any(present));
798 if (present) {
799 css::uno::Reference< css::reflection::XIdlField2 > field(
800 type->getField("Value"), css::uno::UNO_QUERY_THROW);
801 field->set(
802 strct,
803 wrapValue(
804 object, value, field->getType(), wrapAmbiguous,
805 isAmbiguous, wrapDefaulted, isDefaulted, false));
807 } catch (css::lang::IllegalArgumentException & e) {
808 css::uno::Any anyEx = cppu::getCaughtException();
809 throw css::lang::WrappedTargetRuntimeException(
810 "unexpected com.sun.star.lang.IllegalArgumentException: "
811 + e.Message,
812 object, anyEx );
813 } catch (css::lang::IllegalAccessException & e) {
814 css::uno::Any anyEx = cppu::getCaughtException();
815 throw css::lang::WrappedTargetRuntimeException(
816 "unexpected com.sun.star.lang.IllegalAccessException: "
817 + e.Message,
818 object, anyEx );
820 return strct;
822 if (wrapAmbiguous || wrapDefaulted || wrapOptional) {
823 throw css::uno::RuntimeException(
824 "unexpected type of attribute", object);
826 return value;
829 PropertySetMixinImpl::PropertySetMixinImpl(
830 css::uno::Reference< css::uno::XComponentContext > const & context,
831 Implements implements,
832 css::uno::Sequence< OUString > const & absentOptional,
833 css::uno::Type const & type)
835 m_impl = new Impl(context, implements, absentOptional, type);
836 m_impl->acquire();
839 PropertySetMixinImpl::~PropertySetMixinImpl() {
840 m_impl->release();
843 void PropertySetMixinImpl::checkUnknown(OUString const & propertyName) {
844 if (!propertyName.isEmpty()) {
845 m_impl->get(
846 static_cast< css::beans::XPropertySet * >(this), propertyName);
850 void PropertySetMixinImpl::prepareSet(
851 OUString const & propertyName, css::uno::Any const & oldValue,
852 css::uno::Any const & newValue, BoundListeners * boundListeners)
854 Impl::PropertyMap::const_iterator it(m_impl->properties.find(propertyName));
855 assert(it != m_impl->properties.end());
856 Impl::VetoListenerBag specificVeto;
857 Impl::VetoListenerBag unspecificVeto;
859 std::scoped_lock g(m_impl->mutex);
860 if (m_impl->disposed) {
861 throw css::lang::DisposedException(
862 "disposed", static_cast< css::beans::XPropertySet * >(this));
864 if ((it->second.property.Attributes
865 & css::beans::PropertyAttribute::CONSTRAINED)
866 != 0)
868 Impl::VetoListenerMap::const_iterator i(
869 m_impl->vetoListeners.find(propertyName));
870 if (i != m_impl->vetoListeners.end()) {
871 specificVeto = i->second;
873 i = m_impl->vetoListeners.find("");
874 if (i != m_impl->vetoListeners.end()) {
875 unspecificVeto = i->second;
878 if ((it->second.property.Attributes
879 & css::beans::PropertyAttribute::BOUND)
880 != 0)
882 assert(boundListeners != nullptr);
883 Impl::BoundListenerMap::const_iterator i(
884 m_impl->boundListeners.find(propertyName));
885 if (i != m_impl->boundListeners.end()) {
886 boundListeners->m_impl->specificListeners = i->second;
888 i = m_impl->boundListeners.find("");
889 if (i != m_impl->boundListeners.end()) {
890 boundListeners->m_impl->unspecificListeners = i->second;
894 if ((it->second.property.Attributes
895 & css::beans::PropertyAttribute::CONSTRAINED)
896 != 0)
898 css::beans::PropertyChangeEvent event(
899 static_cast< css::beans::XPropertySet * >(this), propertyName,
900 false, it->second.property.Handle, oldValue, newValue);
901 for (auto& rxVetoListener : specificVeto)
903 try {
904 rxVetoListener->vetoableChange(event);
905 } catch (css::lang::DisposedException &) {}
907 for (auto& rxVetoListener : unspecificVeto)
909 try {
910 rxVetoListener->vetoableChange(event);
911 } catch (css::lang::DisposedException &) {}
914 if ((it->second.property.Attributes & css::beans::PropertyAttribute::BOUND)
915 != 0)
917 assert(boundListeners != nullptr);
918 boundListeners->m_impl->event = css::beans::PropertyChangeEvent(
919 static_cast< css::beans::XPropertySet * >(this), propertyName,
920 false, it->second.property.Handle, oldValue, newValue);
924 void PropertySetMixinImpl::dispose() {
925 Impl::BoundListenerMap boundListeners;
926 Impl::VetoListenerMap vetoListeners;
928 std::scoped_lock g(m_impl->mutex);
929 boundListeners.swap(m_impl->boundListeners);
930 vetoListeners.swap(m_impl->vetoListeners);
931 m_impl->disposed = true;
933 css::lang::EventObject event(
934 static_cast< css::beans::XPropertySet * >(this));
935 for (const auto& rEntry : boundListeners)
937 for (auto& rxBoundListener : rEntry.second)
939 rxBoundListener->disposing(event);
942 for (const auto& rEntry : vetoListeners)
944 for (auto& rxVetoListener : rEntry.second)
946 rxVetoListener->disposing(event);
951 css::uno::Any PropertySetMixinImpl::queryInterface(css::uno::Type const & type)
953 if ((m_impl->implements & IMPLEMENTS_PROPERTY_SET) != 0
954 && type == cppu::UnoType<css::beans::XPropertySet>::get())
956 css::uno::Reference< css::uno::XInterface > ifc(
957 static_cast< css::beans::XPropertySet * >(this));
958 return css::uno::Any(&ifc, type);
960 if ((m_impl->implements & IMPLEMENTS_FAST_PROPERTY_SET) != 0
961 && type == cppu::UnoType<css::beans::XFastPropertySet>::get())
963 css::uno::Reference< css::uno::XInterface > ifc(
964 static_cast< css::beans::XFastPropertySet * >(this));
965 return css::uno::Any(&ifc, type);
967 if ((m_impl->implements & IMPLEMENTS_PROPERTY_ACCESS) != 0
968 && type == cppu::UnoType<css::beans::XPropertyAccess>::get())
970 css::uno::Reference< css::uno::XInterface > ifc(
971 static_cast< css::beans::XPropertyAccess * >(this));
972 return css::uno::Any(&ifc, type);
974 return css::uno::Any();
977 css::uno::Reference< css::beans::XPropertySetInfo >
978 PropertySetMixinImpl::getPropertySetInfo()
980 return new Info(m_impl);
983 void PropertySetMixinImpl::setPropertyValue(
984 OUString const & propertyName, css::uno::Any const & value)
986 m_impl->setProperty(
987 static_cast< css::beans::XPropertySet * >(this), propertyName, value,
988 false, false, 1);
991 css::uno::Any PropertySetMixinImpl::getPropertyValue(
992 OUString const & propertyName)
994 return m_impl->getProperty(
995 static_cast< css::beans::XPropertySet * >(this), propertyName, nullptr);
998 void PropertySetMixinImpl::addPropertyChangeListener(
999 OUString const & propertyName,
1000 css::uno::Reference< css::beans::XPropertyChangeListener > const & listener)
1002 css::uno::Reference< css::beans::XPropertyChangeListener >(
1003 listener, css::uno::UNO_SET_THROW); // reject NULL listener
1004 checkUnknown(propertyName);
1005 bool disposed;
1007 std::scoped_lock g(m_impl->mutex);
1008 disposed = m_impl->disposed;
1009 if (!disposed) {
1010 m_impl->boundListeners[propertyName].insert(listener);
1013 if (disposed) {
1014 listener->disposing(
1015 css::lang::EventObject(
1016 static_cast< css::beans::XPropertySet * >(this)));
1020 void PropertySetMixinImpl::removePropertyChangeListener(
1021 OUString const & propertyName,
1022 css::uno::Reference< css::beans::XPropertyChangeListener > const & listener)
1024 assert(listener.is());
1025 checkUnknown(propertyName);
1026 std::scoped_lock g(m_impl->mutex);
1027 Impl::BoundListenerMap::iterator i(
1028 m_impl->boundListeners.find(propertyName));
1029 if (i != m_impl->boundListeners.end()) {
1030 BoundListenerBag::iterator j(i->second.find(listener));
1031 if (j != i->second.end()) {
1032 i->second.erase(j);
1037 void PropertySetMixinImpl::addVetoableChangeListener(
1038 OUString const & propertyName,
1039 css::uno::Reference< css::beans::XVetoableChangeListener > const & listener)
1041 css::uno::Reference< css::beans::XVetoableChangeListener >(
1042 listener, css::uno::UNO_SET_THROW); // reject NULL listener
1043 checkUnknown(propertyName);
1044 bool disposed;
1046 std::scoped_lock g(m_impl->mutex);
1047 disposed = m_impl->disposed;
1048 if (!disposed) {
1049 m_impl->vetoListeners[propertyName].insert(listener);
1052 if (disposed) {
1053 listener->disposing(
1054 css::lang::EventObject(
1055 static_cast< css::beans::XPropertySet * >(this)));
1059 void PropertySetMixinImpl::removeVetoableChangeListener(
1060 OUString const & propertyName,
1061 css::uno::Reference< css::beans::XVetoableChangeListener > const & listener)
1063 assert(listener.is());
1064 checkUnknown(propertyName);
1065 std::scoped_lock g(m_impl->mutex);
1066 Impl::VetoListenerMap::iterator i(m_impl->vetoListeners.find(propertyName));
1067 if (i != m_impl->vetoListeners.end()) {
1068 Impl::VetoListenerBag::iterator j(i->second.find(listener));
1069 if (j != i->second.end()) {
1070 i->second.erase(j);
1075 void PropertySetMixinImpl::setFastPropertyValue(
1076 sal_Int32 handle, css::uno::Any const & value)
1078 m_impl->setProperty(
1079 static_cast< css::beans::XPropertySet * >(this),
1080 m_impl->translateHandle(
1081 static_cast< css::beans::XPropertySet * >(this), handle),
1082 value, false, false, 1);
1085 css::uno::Any PropertySetMixinImpl::getFastPropertyValue(sal_Int32 handle)
1087 return m_impl->getProperty(
1088 static_cast< css::beans::XPropertySet * >(this),
1089 m_impl->translateHandle(
1090 static_cast< css::beans::XPropertySet * >(this), handle),
1091 nullptr);
1094 css::uno::Sequence< css::beans::PropertyValue >
1095 PropertySetMixinImpl::getPropertyValues()
1097 css::uno::Sequence< css::beans::PropertyValue > s(
1098 m_impl->handleMap.getLength());
1099 auto r = asNonConstRange(s);
1100 sal_Int32 n = 0;
1101 for (sal_Int32 i = 0; i < m_impl->handleMap.getLength(); ++i) {
1102 try {
1103 r[n].Value = m_impl->getProperty(
1104 static_cast< css::beans::XPropertySet * >(this),
1105 m_impl->handleMap[i], &r[n].State);
1106 } catch (css::beans::UnknownPropertyException &) {
1107 continue;
1108 } catch (css::lang::WrappedTargetException & e) {
1109 throw css::lang::WrappedTargetRuntimeException(
1110 e.Message, static_cast< css::beans::XPropertySet * >(this),
1111 e.TargetException);
1113 r[n].Name = m_impl->handleMap[i];
1114 r[n].Handle = i;
1115 ++n;
1117 s.realloc(n);
1118 return s;
1121 void PropertySetMixinImpl::setPropertyValues(
1122 css::uno::Sequence< css::beans::PropertyValue > const & props)
1124 for (const auto & p : props) {
1125 if (p.Handle != -1
1126 && (p.Name
1127 != m_impl->translateHandle(
1128 static_cast< css::beans::XPropertySet * >(this),
1129 p.Handle)))
1131 throw css::beans::UnknownPropertyException(
1132 ("name " + p.Name + " does not match handle "
1133 + OUString::number(p.Handle)),
1134 static_cast< css::beans::XPropertySet * >(this));
1136 m_impl->setProperty(
1137 static_cast< css::beans::XPropertySet * >(this), p.Name,
1138 p.Value,
1139 p.State == css::beans::PropertyState_AMBIGUOUS_VALUE,
1140 p.State == css::beans::PropertyState_DEFAULT_VALUE, 0);
1144 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */