/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <algorithm>
#include <cassert>
#include <map>
#include <mutex>
#include <set>
#include <utility>
#include <vector>
#include <com/sun/star/beans/Property.hpp>
#include <com/sun/star/beans/PropertyChangeEvent.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/PropertyVetoException.hpp>
#include <com/sun/star/beans/UnknownPropertyException.hpp>
#include <com/sun/star/beans/XFastPropertySet.hpp>
#include <com/sun/star/beans/XPropertyAccess.hpp>
#include <com/sun/star/beans/XPropertyChangeListener.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/beans/XVetoableChangeListener.hpp>
#include <com/sun/star/container/NoSuchElementException.hpp>
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/EventObject.hpp>
#include <com/sun/star/lang/IllegalAccessException.hpp>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/lang/WrappedTargetException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/reflection/XCompoundTypeDescription.hpp>
#include <com/sun/star/reflection/XIdlClass.hpp>
#include <com/sun/star/reflection/XIdlField2.hpp>
#include <com/sun/star/reflection/XIndirectTypeDescription.hpp>
#include <com/sun/star/reflection/XInterfaceAttributeTypeDescription2.hpp>
#include <com/sun/star/reflection/XInterfaceMemberTypeDescription.hpp>
#include <com/sun/star/reflection/XInterfaceTypeDescription2.hpp>
#include <com/sun/star/reflection/XStructTypeDescription.hpp>
#include <com/sun/star/reflection/XTypeDescription.hpp>
#include <com/sun/star/reflection/theCoreReflection.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/uno/Type.hxx>
#include <com/sun/star/uno/TypeClass.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/uno/XInterface.hpp>
#include <cppu/unotype.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/propertysetmixin.hxx>
#include <cppuhelper/weak.hxx>
#include <rtl/ref.hxx>
#include <rtl/ustring.hxx>
#include <sal/types.h>
#include <salhelper/simplereferenceobject.hxx>
using cppu::PropertySetMixinImpl;
namespace {
struct PropertyData {
explicit PropertyData(
css::beans::Property theProperty, bool thePresent):
property(std::move(theProperty)), present(thePresent) {}
css::beans::Property property;
bool present;
};
struct Data: public salhelper::SimpleReferenceObject {
typedef std::map< OUString, PropertyData > PropertyMap;
PropertyMap properties;
PropertyMap::const_iterator get(
css::uno::Reference< css::uno::XInterface > const & object,
OUString const & name) const;
protected:
void initProperties(
css::uno::Reference< css::reflection::XTypeDescription > const & type,
css::uno::Sequence< OUString > const & absentOptional,
std::vector< OUString > * handleNames)
{
std::set<OUString> seen;
initProperties(type, absentOptional, handleNames, &seen);
}
private:
void initProperties(
css::uno::Reference< css::reflection::XTypeDescription > const & type,
css::uno::Sequence< OUString > const & absentOptional,
std::vector< OUString > * handleNames, std::set<OUString> * seen);
static css::uno::Reference< css::reflection::XTypeDescription >
resolveTypedefs(
css::uno::Reference< css::reflection::XTypeDescription > const & type);
};
Data::PropertyMap::const_iterator Data::get(
css::uno::Reference< css::uno::XInterface > const & object,
OUString const & name) const
{
PropertyMap::const_iterator i(properties.find(name));
if (i == properties.end() || !i->second.present) {
throw css::beans::UnknownPropertyException(name, object);
}
return i;
}
void Data::initProperties(
css::uno::Reference< css::reflection::XTypeDescription > const & type,
css::uno::Sequence< OUString > const & absentOptional,
std::vector< OUString > * handleNames, std::set<OUString> * seen)
{
css::uno::Reference< css::reflection::XInterfaceTypeDescription2 > ifc(
resolveTypedefs(type), css::uno::UNO_QUERY_THROW);
if (!seen->insert(ifc->getName()).second)
return;
const css::uno::Sequence<
css::uno::Reference< css::reflection::XTypeDescription > > bases(
ifc->getBaseTypes());
for (const auto & i : bases) {
initProperties(i, absentOptional, handleNames, seen);
}
const css::uno::Sequence<
css::uno::Reference<
css::reflection::XInterfaceMemberTypeDescription > > members(
ifc->getMembers());
for (const auto & m : members) {
if (m->getTypeClass()
== css::uno::TypeClass_INTERFACE_ATTRIBUTE)
{
css::uno::Reference<
css::reflection::XInterfaceAttributeTypeDescription2 > attr(
m, css::uno::UNO_QUERY_THROW);
sal_Int16 attrAttribs = 0;
if (attr->isBound()) {
attrAttribs |= css::beans::PropertyAttribute::BOUND;
}
bool bSetUnknown = false;
if (attr->isReadOnly()) {
attrAttribs |= css::beans::PropertyAttribute::READONLY;
bSetUnknown = true;
}
css::uno::Sequence<
css::uno::Reference<
css::reflection::XCompoundTypeDescription > > excs(
attr->getGetExceptions());
bool bGetUnknown = false;
//XXX Special interpretation of getter/setter exceptions only
// works if the specified exceptions are of the exact type, not
// of a supertype:
for (const auto & ex : excs) {
if ( ex->getName() == "com.sun.star.beans.UnknownPropertyException" )
{
bGetUnknown = true;
break;
}
}
excs = attr->getSetExceptions();
for (const auto & ex : excs) {
if ( ex->getName() == "com.sun.star.beans.UnknownPropertyException" )
{
bSetUnknown = true;
} else if ( ex->getName() == "com.sun.star.beans.PropertyVetoException" )
{
attrAttribs
|= css::beans::PropertyAttribute::CONSTRAINED;
}
}
if (bGetUnknown && bSetUnknown) {
attrAttribs |= css::beans::PropertyAttribute::OPTIONAL;
}
css::uno::Reference< css::reflection::XTypeDescription > t(
attr->getType());
for (;;)
{
t = resolveTypedefs(t);
sal_Int16 n;
if (t->getName().startsWith(
"com.sun.star.beans.Ambiguous<"))
{
n = css::beans::PropertyAttribute::MAYBEAMBIGUOUS;
} else if (t->getName().startsWith(
"com.sun.star.beans.Defaulted<"))
{
n = css::beans::PropertyAttribute::MAYBEDEFAULT;
} else if (t->getName().startsWith(
"com.sun.star.beans.Optional<"))
{
n = css::beans::PropertyAttribute::MAYBEVOID;
} else {
break;
}
if ((attrAttribs & n) != 0) {
break;
}
attrAttribs |= n;
const css::uno::Sequence<
css::uno::Reference< css::reflection::XTypeDescription > >
args(
css::uno::Reference<
css::reflection::XStructTypeDescription >(
t, css::uno::UNO_QUERY_THROW)->
getTypeArguments());
if (args.getLength() != 1) {
throw css::uno::RuntimeException(
u"inconsistent UNO type registry"_ustr);
}
t = args[0];
}
std::vector< OUString >::size_type handles
= handleNames->size();
if (handles > SAL_MAX_INT32) {
throw css::uno::RuntimeException(
u"interface type has too many attributes"_ustr);
}
OUString name(m->getMemberName());
if (!properties.emplace(
name,
PropertyData(
css::beans::Property(
name, static_cast< sal_Int32 >(handles),
css::uno::Type(
t->getTypeClass(), t->getName()),
attrAttribs),
(std::find(absentOptional.begin(), absentOptional.end(), name)
== absentOptional.end()))).
second)
{
throw css::uno::RuntimeException(
u"inconsistent UNO type registry"_ustr);
}
handleNames->push_back(name);
}
}
}
css::uno::Reference< css::reflection::XTypeDescription > Data::resolveTypedefs(
css::uno::Reference< css::reflection::XTypeDescription > const & type)
{
css::uno::Reference< css::reflection::XTypeDescription > t(type);
while (t->getTypeClass() == css::uno::TypeClass_TYPEDEF) {
t = css::uno::Reference< css::reflection::XIndirectTypeDescription >(
t, css::uno::UNO_QUERY_THROW)->getReferencedType();
}
return t;
}
class Info: public cppu::WeakImplHelper< css::beans::XPropertySetInfo > {
public:
explicit Info(Data * data): m_data(data) {}
virtual css::uno::Sequence< css::beans::Property > SAL_CALL getProperties() override;
virtual css::beans::Property SAL_CALL getPropertyByName(
OUString const & name) override;
virtual sal_Bool SAL_CALL hasPropertyByName(OUString const & name) override;
private:
rtl::Reference< Data > m_data;
};
css::uno::Sequence< css::beans::Property > Info::getProperties()
{
assert(m_data->properties.size() <= SAL_MAX_INT32);
css::uno::Sequence< css::beans::Property > s(
static_cast< sal_Int32 >(m_data->properties.size()));
auto r = asNonConstRange(s);
sal_Int32 n = 0;
for (const auto& rEntry : m_data->properties)
{
if (rEntry.second.present) {
r[n++] = rEntry.second.property;
}
}
s.realloc(n);
return s;
}
css::beans::Property Info::getPropertyByName(OUString const & name)
{
return m_data->get(static_cast< cppu::OWeakObject * >(this), name)->
second.property;
}
sal_Bool Info::hasPropertyByName(OUString const & name)
{
Data::PropertyMap::iterator i(m_data->properties.find(name));
return i != m_data->properties.end() && i->second.present;
}
typedef
std::multiset< css::uno::Reference< css::beans::XPropertyChangeListener > >
BoundListenerBag;
}
class PropertySetMixinImpl::BoundListeners::Impl {
public:
BoundListenerBag specificListeners;
BoundListenerBag unspecificListeners;
css::beans::PropertyChangeEvent event;
};
PropertySetMixinImpl::BoundListeners::BoundListeners(): m_impl(new Impl) {}
PropertySetMixinImpl::BoundListeners::~BoundListeners() {
delete m_impl;
}
void PropertySetMixinImpl::BoundListeners::notify() const {
for (const auto& rxListener : m_impl->specificListeners)
{
try {
rxListener->propertyChange(m_impl->event);
} catch (css::lang::DisposedException &) {}
}
for (const auto& rxListener : m_impl->unspecificListeners)
{
try {
rxListener->propertyChange(m_impl->event);
} catch (css::lang::DisposedException &) {}
}
}
class PropertySetMixinImpl::Impl: public Data {
public:
Impl(
css::uno::Reference< css::uno::XComponentContext > const & context,
Implements theImplements,
css::uno::Sequence< OUString > const & absentOptional,
css::uno::Type const & type);
OUString const & translateHandle(
css::uno::Reference< css::uno::XInterface > const & object,
sal_Int32 handle) const;
void setProperty(
css::uno::Reference< css::uno::XInterface > const & object,
OUString const & name, css::uno::Any const & value,
bool isAmbiguous, bool isDefaulted, sal_Int16 illegalArgumentPosition)
const;
css::uno::Any getProperty(
css::uno::Reference< css::uno::XInterface > const & object,
OUString const & name, css::beans::PropertyState * state) const;
PropertySetMixinImpl::Implements implements;
css::uno::Sequence< OUString > handleMap;
typedef std::map< OUString, BoundListenerBag > BoundListenerMap;
typedef
std::multiset< css::uno::Reference< css::beans::XVetoableChangeListener > >
VetoListenerBag;
typedef std::map< OUString, VetoListenerBag > VetoListenerMap;
mutable std::mutex mutex;
BoundListenerMap boundListeners;
VetoListenerMap vetoListeners;
bool disposed;
private:
css::uno::Reference< css::reflection::XIdlClass > getReflection(
OUString const & typeName) const;
static css::uno::Any wrapValue(
css::uno::Reference< css::uno::XInterface > const & object,
css::uno::Any const & value,
css::uno::Reference< css::reflection::XIdlClass > const & type,
bool wrapAmbiguous, bool isAmbiguous, bool wrapDefaulted,
bool isDefaulted, bool wrapOptional);
css::uno::Reference< css::uno::XComponentContext > const & m_context;
css::uno::Type m_type;
css::uno::Reference< css::reflection::XIdlClass > m_idlClass;
};
PropertySetMixinImpl::Impl::Impl(
css::uno::Reference< css::uno::XComponentContext > const & context,
Implements theImplements,
css::uno::Sequence< OUString > const & absentOptional,
css::uno::Type const & type):
implements(theImplements), disposed(false), m_context(context),
m_type(type)
{
assert(context.is());
assert(
(implements
& ~(IMPLEMENTS_PROPERTY_SET | IMPLEMENTS_FAST_PROPERTY_SET
| IMPLEMENTS_PROPERTY_ACCESS))
== 0);
m_idlClass = getReflection(m_type.getTypeName());
css::uno::Reference< css::reflection::XTypeDescription > ifc;
try {
ifc.set(
css::uno::Reference< css::container::XHierarchicalNameAccess >(
m_context->getValueByName(
u"/singletons/com.sun.star.reflection."
"theTypeDescriptionManager"_ustr),
css::uno::UNO_QUERY_THROW)->getByHierarchicalName(
m_type.getTypeName()),
css::uno::UNO_QUERY_THROW);
} catch (css::container::NoSuchElementException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.container.NoSuchElementException: "
+ e.Message,
nullptr, anyEx );
}
std::vector< OUString > handleNames;
initProperties(ifc, absentOptional, &handleNames);
std::vector< OUString >::size_type size = handleNames.size();
assert(size <= SAL_MAX_INT32);
handleMap.realloc(static_cast< sal_Int32 >(size));
std::copy(handleNames.begin(), handleNames.end(), handleMap.getArray());
}
OUString const & PropertySetMixinImpl::Impl::translateHandle(
css::uno::Reference< css::uno::XInterface > const & object,
sal_Int32 handle) const
{
if (handle < 0 || handle >= handleMap.getLength()) {
throw css::beans::UnknownPropertyException(
"bad handle " + OUString::number(handle), object);
}
return handleMap[handle];
}
void PropertySetMixinImpl::Impl::setProperty(
css::uno::Reference< css::uno::XInterface > const & object,
OUString const & name, css::uno::Any const & value, bool isAmbiguous,
bool isDefaulted, sal_Int16 illegalArgumentPosition) const
{
PropertyMap::const_iterator i(properties.find(name));
if (i == properties.end()) {
throw css::beans::UnknownPropertyException(name, object);
}
if ((isAmbiguous
&& ((i->second.property.Attributes
& css::beans::PropertyAttribute::MAYBEAMBIGUOUS)
== 0))
|| (isDefaulted
&& ((i->second.property.Attributes
& css::beans::PropertyAttribute::MAYBEDEFAULT)
== 0)))
{
throw css::lang::IllegalArgumentException(
("flagging as ambiguous/defaulted non-ambiguous/defaulted property "
+ name),
object, illegalArgumentPosition);
}
css::uno::Reference< css::reflection::XIdlField2 > f(
m_idlClass->getField(name), css::uno::UNO_QUERY_THROW);
css::uno::Any o(object->queryInterface(m_type));
css::uno::Any v(
wrapValue(
object, value,
(css::uno::Reference< css::reflection::XIdlField2 >(
m_idlClass->getField(name), css::uno::UNO_QUERY_THROW)->
getType()),
((i->second.property.Attributes
& css::beans::PropertyAttribute::MAYBEAMBIGUOUS)
!= 0),
isAmbiguous,
((i->second.property.Attributes
& css::beans::PropertyAttribute::MAYBEDEFAULT)
!= 0),
isDefaulted,
((i->second.property.Attributes
& css::beans::PropertyAttribute::MAYBEVOID)
!= 0)));
try {
f->set(o, v);
} catch (css::lang::IllegalArgumentException & e) {
if (e.ArgumentPosition == 1) {
throw css::lang::IllegalArgumentException(
e.Message, object, illegalArgumentPosition);
} else {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalArgumentException: "
+ e.Message,
object, anyEx );
}
} catch (css::lang::IllegalAccessException &) {
//TODO Clarify whether PropertyVetoException is the correct exception
// to throw when trying to set a read-only property:
throw css::beans::PropertyVetoException(
"cannot set read-only property " + name, object);
} catch (css::lang::WrappedTargetRuntimeException & e) {
//FIXME A WrappedTargetRuntimeException from XIdlField2.get is not
// guaranteed to originate directly within XIdlField2.get (and thus have
// the expected semantics); it might also be passed through from lower
// layers.
if (e.TargetException.isExtractableTo(
cppu::UnoType<css::beans::UnknownPropertyException>::get())
&& ((i->second.property.Attributes
& css::beans::PropertyAttribute::OPTIONAL)
!= 0))
{
throw css::beans::UnknownPropertyException(name, object);
} else if (e.TargetException.isExtractableTo(
cppu::UnoType<css::beans::PropertyVetoException>::get())
&& ((i->second.property.Attributes
& css::beans::PropertyAttribute::CONSTRAINED)
!= 0))
{
css::beans::PropertyVetoException exc;
e.TargetException >>= exc;
if (exc.Message.isEmpty() )
throw css::beans::PropertyVetoException("Invalid " + name, object);
else
throw exc;
} else {
throw css::lang::WrappedTargetException(
e.Message, object, e.TargetException);
}
}
}
css::uno::Any PropertySetMixinImpl::Impl::getProperty(
css::uno::Reference< css::uno::XInterface > const & object,
OUString const & name, css::beans::PropertyState * state) const
{
PropertyMap::const_iterator i(properties.find(name));
if (i == properties.end()) {
throw css::beans::UnknownPropertyException(name, object);
}
css::uno::Reference< css::reflection::XIdlField2 > field(
m_idlClass->getField(name), css::uno::UNO_QUERY_THROW);
css::uno::Any value;
try {
value = field->get(object->queryInterface(m_type));
} catch (css::lang::IllegalArgumentException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalArgumentException: "
+ e.Message,
object, anyEx );
} catch (css::lang::WrappedTargetRuntimeException & e) {
//FIXME A WrappedTargetRuntimeException from XIdlField2.get is not
// guaranteed to originate directly within XIdlField2.get (and thus have
// the expected semantics); it might also be passed through from lower
// layers.
if (e.TargetException.isExtractableTo(
cppu::UnoType<css::beans::UnknownPropertyException>::get())
&& ((i->second.property.Attributes
& css::beans::PropertyAttribute::OPTIONAL)
!= 0))
{
throw css::beans::UnknownPropertyException(name, object);
} else {
throw css::lang::WrappedTargetException(
e.Message, object, e.TargetException);
}
}
bool undoAmbiguous
= ((i->second.property.Attributes
& css::beans::PropertyAttribute::MAYBEAMBIGUOUS)
!= 0);
bool undoDefaulted
= ((i->second.property.Attributes
& css::beans::PropertyAttribute::MAYBEDEFAULT)
!= 0);
bool undoOptional
= ((i->second.property.Attributes
& css::beans::PropertyAttribute::MAYBEVOID)
!= 0);
bool isAmbiguous = false;
bool isDefaulted = false;
while (undoAmbiguous || undoDefaulted || undoOptional) {
if (undoAmbiguous
&& value.getValueTypeName().startsWith(
"com.sun.star.beans.Ambiguous<"))
{
css::uno::Reference< css::reflection::XIdlClass > ambiguous(
getReflection(value.getValueTypeName()));
try {
if (!(css::uno::Reference< css::reflection::XIdlField2 >(
ambiguous->getField(u"IsAmbiguous"_ustr),
css::uno::UNO_QUERY_THROW)->get(value)
>>= isAmbiguous))
{
throw css::uno::RuntimeException(
(u"unexpected type of com.sun.star.beans.Ambiguous"
" IsAmbiguous member"_ustr),
object);
}
value = css::uno::Reference< css::reflection::XIdlField2 >(
ambiguous->getField(u"Value"_ustr), css::uno::UNO_QUERY_THROW)->
get(value);
} catch (css::lang::IllegalArgumentException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalArgumentException: "
+ e.Message,
object, anyEx );
}
undoAmbiguous = false;
} else if (undoDefaulted
&& value.getValueTypeName().startsWith(
"com.sun.star.beans.Defaulted<"))
{
css::uno::Reference< css::reflection::XIdlClass > defaulted(
getReflection(value.getValueTypeName()));
try {
if (!(css::uno::Reference< css::reflection::XIdlField2 >(
defaulted->getField(u"IsDefaulted"_ustr),
css::uno::UNO_QUERY_THROW)->get(value)
>>= isDefaulted))
{
throw css::uno::RuntimeException(
(u"unexpected type of com.sun.star.beans.Defaulted"
" IsDefaulted member"_ustr),
object);
}
value = css::uno::Reference< css::reflection::XIdlField2 >(
defaulted->getField(u"Value"_ustr), css::uno::UNO_QUERY_THROW)->
get(value);
} catch (css::lang::IllegalArgumentException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalArgumentException: "
+ e.Message,
object, anyEx );
}
undoDefaulted = false;
} else if (undoOptional
&& value.getValueTypeName().startsWith(
"com.sun.star.beans.Optional<"))
{
css::uno::Reference< css::reflection::XIdlClass > optional(
getReflection(value.getValueTypeName()));
try {
bool present = false;
if (!(css::uno::Reference< css::reflection::XIdlField2 >(
optional->getField(u"IsPresent"_ustr),
css::uno::UNO_QUERY_THROW)->get(value)
>>= present))
{
throw css::uno::RuntimeException(
(u"unexpected type of com.sun.star.beans.Optional"
" IsPresent member"_ustr),
object);
}
if (!present) {
value.clear();
break;
}
value = css::uno::Reference< css::reflection::XIdlField2 >(
optional->getField(u"Value"_ustr), css::uno::UNO_QUERY_THROW)->
get(value);
} catch (css::lang::IllegalArgumentException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalArgumentException: "
+ e.Message,
object, anyEx );
}
undoOptional = false;
} else {
throw css::uno::RuntimeException(
"unexpected type of attribute " + name, object);
}
}
if (state != nullptr) {
//XXX If isAmbiguous && isDefaulted, arbitrarily choose AMBIGUOUS_VALUE
// over DEFAULT_VALUE:
*state = isAmbiguous
? css::beans::PropertyState_AMBIGUOUS_VALUE
: isDefaulted
? css::beans::PropertyState_DEFAULT_VALUE
: css::beans::PropertyState_DIRECT_VALUE;
}
return value;
}
css::uno::Reference< css::reflection::XIdlClass >
PropertySetMixinImpl::Impl::getReflection(OUString const & typeName) const
{
return css::uno::Reference< css::reflection::XIdlClass >(
css::reflection::theCoreReflection::get(m_context)->forName(typeName),
css::uno::UNO_SET_THROW);
}
css::uno::Any PropertySetMixinImpl::Impl::wrapValue(
css::uno::Reference< css::uno::XInterface > const & object,
css::uno::Any const & value,
css::uno::Reference< css::reflection::XIdlClass > const & type,
bool wrapAmbiguous, bool isAmbiguous, bool wrapDefaulted, bool isDefaulted,
bool wrapOptional)
{
assert(wrapAmbiguous || !isAmbiguous);
assert(wrapDefaulted || !isDefaulted);
if (wrapAmbiguous
&& type->getName().startsWith("com.sun.star.beans.Ambiguous<"))
{
css::uno::Any strct;
type->createObject(strct);
try {
css::uno::Reference< css::reflection::XIdlField2 > field(
type->getField(u"Value"_ustr), css::uno::UNO_QUERY_THROW);
field->set(
strct,
wrapValue(
object, value, field->getType(), false, false,
wrapDefaulted, isDefaulted, wrapOptional));
css::uno::Reference< css::reflection::XIdlField2 >(
type->getField(u"IsAmbiguous"_ustr), css::uno::UNO_QUERY_THROW)->set(
strct, css::uno::Any(isAmbiguous));
} catch (css::lang::IllegalArgumentException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalArgumentException: "
+ e.Message,
object, anyEx );
} catch (css::lang::IllegalAccessException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalAccessException: "
+ e.Message,
object, anyEx );
}
return strct;
}
if (wrapDefaulted
&& type->getName().startsWith("com.sun.star.beans.Defaulted<"))
{
css::uno::Any strct;
type->createObject(strct);
try {
css::uno::Reference< css::reflection::XIdlField2 > field(
type->getField(u"Value"_ustr), css::uno::UNO_QUERY_THROW);
field->set(
strct,
wrapValue(
object, value, field->getType(), wrapAmbiguous, isAmbiguous,
false, false, wrapOptional));
css::uno::Reference< css::reflection::XIdlField2 >(
type->getField(u"IsDefaulted"_ustr), css::uno::UNO_QUERY_THROW)->set(
strct, css::uno::Any(isDefaulted));
} catch (css::lang::IllegalArgumentException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalArgumentException: "
+ e.Message,
object, anyEx );
} catch (css::lang::IllegalAccessException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalAccessException: "
+ e.Message,
object, anyEx );
}
return strct;
}
if (wrapOptional
&& type->getName().startsWith("com.sun.star.beans.Optional<"))
{
css::uno::Any strct;
type->createObject(strct);
bool present = value.hasValue();
try {
css::uno::Reference< css::reflection::XIdlField2 >(
type->getField(u"IsPresent"_ustr), css::uno::UNO_QUERY_THROW)->set(
strct, css::uno::Any(present));
if (present) {
css::uno::Reference< css::reflection::XIdlField2 > field(
type->getField(u"Value"_ustr), css::uno::UNO_QUERY_THROW);
field->set(
strct,
wrapValue(
object, value, field->getType(), wrapAmbiguous,
isAmbiguous, wrapDefaulted, isDefaulted, false));
}
} catch (css::lang::IllegalArgumentException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalArgumentException: "
+ e.Message,
object, anyEx );
} catch (css::lang::IllegalAccessException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"unexpected com.sun.star.lang.IllegalAccessException: "
+ e.Message,
object, anyEx );
}
return strct;
}
if (wrapAmbiguous || wrapDefaulted || wrapOptional) {
throw css::uno::RuntimeException(
u"unexpected type of attribute"_ustr, object);
}
return value;
}
PropertySetMixinImpl::PropertySetMixinImpl(
css::uno::Reference< css::uno::XComponentContext > const & context,
Implements implements,
css::uno::Sequence< OUString > const & absentOptional,
css::uno::Type const & type)
{
m_impl = new Impl(context, implements, absentOptional, type);
m_impl->acquire();
}
PropertySetMixinImpl::~PropertySetMixinImpl() {
m_impl->release();
}
void PropertySetMixinImpl::checkUnknown(OUString const & propertyName) {
if (!propertyName.isEmpty()) {
m_impl->get(
static_cast< css::beans::XPropertySet * >(this), propertyName);
}
}
void PropertySetMixinImpl::prepareSet(
OUString const & propertyName, css::uno::Any const & oldValue,
css::uno::Any const & newValue, BoundListeners * boundListeners)
{
Impl::PropertyMap::const_iterator it(m_impl->properties.find(propertyName));
assert(it != m_impl->properties.end());
Impl::VetoListenerBag specificVeto;
Impl::VetoListenerBag unspecificVeto;
{
std::scoped_lock g(m_impl->mutex);
if (m_impl->disposed) {
throw css::lang::DisposedException(
u"disposed"_ustr, static_cast< css::beans::XPropertySet * >(this));
}
if ((it->second.property.Attributes
& css::beans::PropertyAttribute::CONSTRAINED)
!= 0)
{
Impl::VetoListenerMap::const_iterator i(
m_impl->vetoListeners.find(propertyName));
if (i != m_impl->vetoListeners.end()) {
specificVeto = i->second;
}
i = m_impl->vetoListeners.find(u""_ustr);
if (i != m_impl->vetoListeners.end()) {
unspecificVeto = i->second;
}
}
if ((it->second.property.Attributes
& css::beans::PropertyAttribute::BOUND)
!= 0)
{
assert(boundListeners != nullptr);
Impl::BoundListenerMap::const_iterator i(
m_impl->boundListeners.find(propertyName));
if (i != m_impl->boundListeners.end()) {
boundListeners->m_impl->specificListeners = i->second;
}
i = m_impl->boundListeners.find(u""_ustr);
if (i != m_impl->boundListeners.end()) {
boundListeners->m_impl->unspecificListeners = i->second;
}
}
}
if ((it->second.property.Attributes
& css::beans::PropertyAttribute::CONSTRAINED)
!= 0)
{
css::beans::PropertyChangeEvent event(
static_cast< css::beans::XPropertySet * >(this), propertyName,
false, it->second.property.Handle, oldValue, newValue);
for (auto& rxVetoListener : specificVeto)
{
try {
rxVetoListener->vetoableChange(event);
} catch (css::lang::DisposedException &) {}
}
for (auto& rxVetoListener : unspecificVeto)
{
try {
rxVetoListener->vetoableChange(event);
} catch (css::lang::DisposedException &) {}
}
}
if ((it->second.property.Attributes & css::beans::PropertyAttribute::BOUND)
!= 0)
{
assert(boundListeners != nullptr);
boundListeners->m_impl->event = css::beans::PropertyChangeEvent(
static_cast< css::beans::XPropertySet * >(this), propertyName,
false, it->second.property.Handle, oldValue, newValue);
}
}
void PropertySetMixinImpl::dispose() {
Impl::BoundListenerMap boundListeners;
Impl::VetoListenerMap vetoListeners;
{
std::scoped_lock g(m_impl->mutex);
boundListeners.swap(m_impl->boundListeners);
vetoListeners.swap(m_impl->vetoListeners);
m_impl->disposed = true;
}
css::lang::EventObject event(
static_cast< css::beans::XPropertySet * >(this));
for (const auto& rEntry : boundListeners)
{
for (auto& rxBoundListener : rEntry.second)
{
rxBoundListener->disposing(event);
}
}
for (const auto& rEntry : vetoListeners)
{
for (auto& rxVetoListener : rEntry.second)
{
rxVetoListener->disposing(event);
}
}
}
css::uno::Any PropertySetMixinImpl::queryInterface(css::uno::Type const & type)
{
if ((m_impl->implements & IMPLEMENTS_PROPERTY_SET) != 0
&& type == cppu::UnoType<css::beans::XPropertySet>::get())
{
css::uno::Reference< css::uno::XInterface > ifc(
static_cast< css::beans::XPropertySet * >(this));
return css::uno::Any(&ifc, type);
}
if ((m_impl->implements & IMPLEMENTS_FAST_PROPERTY_SET) != 0
&& type == cppu::UnoType<css::beans::XFastPropertySet>::get())
{
css::uno::Reference< css::uno::XInterface > ifc(
static_cast< css::beans::XFastPropertySet * >(this));
return css::uno::Any(&ifc, type);
}
if ((m_impl->implements & IMPLEMENTS_PROPERTY_ACCESS) != 0
&& type == cppu::UnoType<css::beans::XPropertyAccess>::get())
{
css::uno::Reference< css::uno::XInterface > ifc(
static_cast< css::beans::XPropertyAccess * >(this));
return css::uno::Any(&ifc, type);
}
return css::uno::Any();
}
css::uno::Reference< css::beans::XPropertySetInfo >
PropertySetMixinImpl::getPropertySetInfo()
{
return new Info(m_impl);
}
void PropertySetMixinImpl::setPropertyValue(
OUString const & propertyName, css::uno::Any const & value)
{
m_impl->setProperty(
static_cast< css::beans::XPropertySet * >(this), propertyName, value,
false, false, 1);
}
css::uno::Any PropertySetMixinImpl::getPropertyValue(
OUString const & propertyName)
{
return m_impl->getProperty(
static_cast< css::beans::XPropertySet * >(this), propertyName, nullptr);
}
void PropertySetMixinImpl::addPropertyChangeListener(
OUString const & propertyName,
css::uno::Reference< css::beans::XPropertyChangeListener > const & listener)
{
css::uno::Reference< css::beans::XPropertyChangeListener >(
listener, css::uno::UNO_SET_THROW); // reject NULL listener
checkUnknown(propertyName);
bool disposed;
{
std::scoped_lock g(m_impl->mutex);
disposed = m_impl->disposed;
if (!disposed) {
m_impl->boundListeners[propertyName].insert(listener);
}
}
if (disposed) {
listener->disposing(
css::lang::EventObject(
static_cast< css::beans::XPropertySet * >(this)));
}
}
void PropertySetMixinImpl::removePropertyChangeListener(
OUString const & propertyName,
css::uno::Reference< css::beans::XPropertyChangeListener > const & listener)
{
assert(listener.is());
checkUnknown(propertyName);
std::scoped_lock g(m_impl->mutex);
Impl::BoundListenerMap::iterator i(
m_impl->boundListeners.find(propertyName));
if (i != m_impl->boundListeners.end()) {
BoundListenerBag::iterator j(i->second.find(listener));
if (j != i->second.end()) {
i->second.erase(j);
}
}
}
void PropertySetMixinImpl::addVetoableChangeListener(
OUString const & propertyName,
css::uno::Reference< css::beans::XVetoableChangeListener > const & listener)
{
css::uno::Reference< css::beans::XVetoableChangeListener >(
listener, css::uno::UNO_SET_THROW); // reject NULL listener
checkUnknown(propertyName);
bool disposed;
{
std::scoped_lock g(m_impl->mutex);
disposed = m_impl->disposed;
if (!disposed) {
m_impl->vetoListeners[propertyName].insert(listener);
}
}
if (disposed) {
listener->disposing(
css::lang::EventObject(
static_cast< css::beans::XPropertySet * >(this)));
}
}
void PropertySetMixinImpl::removeVetoableChangeListener(
OUString const & propertyName,
css::uno::Reference< css::beans::XVetoableChangeListener > const & listener)
{
assert(listener.is());
checkUnknown(propertyName);
std::scoped_lock g(m_impl->mutex);
Impl::VetoListenerMap::iterator i(m_impl->vetoListeners.find(propertyName));
if (i != m_impl->vetoListeners.end()) {
Impl::VetoListenerBag::iterator j(i->second.find(listener));
if (j != i->second.end()) {
i->second.erase(j);
}
}
}
void PropertySetMixinImpl::setFastPropertyValue(
sal_Int32 handle, css::uno::Any const & value)
{
m_impl->setProperty(
static_cast< css::beans::XPropertySet * >(this),
m_impl->translateHandle(
static_cast< css::beans::XPropertySet * >(this), handle),
value, false, false, 1);
}
css::uno::Any PropertySetMixinImpl::getFastPropertyValue(sal_Int32 handle)
{
return m_impl->getProperty(
static_cast< css::beans::XPropertySet * >(this),
m_impl->translateHandle(
static_cast< css::beans::XPropertySet * >(this), handle),
nullptr);
}
css::uno::Sequence< css::beans::PropertyValue >
PropertySetMixinImpl::getPropertyValues()
{
css::uno::Sequence< css::beans::PropertyValue > s(
m_impl->handleMap.getLength());
auto r = asNonConstRange(s);
sal_Int32 n = 0;
for (sal_Int32 i = 0; i < m_impl->handleMap.getLength(); ++i) {
try {
r[n].Value = m_impl->getProperty(
static_cast< css::beans::XPropertySet * >(this),
m_impl->handleMap[i], &r[n].State);
} catch (css::beans::UnknownPropertyException &) {
continue;
} catch (css::lang::WrappedTargetException & e) {
throw css::lang::WrappedTargetRuntimeException(
e.Message, static_cast< css::beans::XPropertySet * >(this),
e.TargetException);
}
r[n].Name = m_impl->handleMap[i];
r[n].Handle = i;
++n;
}
s.realloc(n);
return s;
}
void PropertySetMixinImpl::setPropertyValues(
css::uno::Sequence< css::beans::PropertyValue > const & props)
{
for (const auto & p : props) {
if (p.Handle != -1
&& (p.Name
!= m_impl->translateHandle(
static_cast< css::beans::XPropertySet * >(this),
p.Handle)))
{
throw css::beans::UnknownPropertyException(
("name " + p.Name + " does not match handle "
+ OUString::number(p.Handle)),
static_cast< css::beans::XPropertySet * >(this));
}
m_impl->setProperty(
static_cast< css::beans::XPropertySet * >(this), p.Name,
p.Value,
p.State == css::beans::PropertyState_AMBIGUOUS_VALUE,
p.State == css::beans::PropertyState_DEFAULT_VALUE, 0);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V547 Expression '!present' is always true.
↑ V547 Expression 'isAmbiguous' is always false.
↑ V547 Expression 'isDefaulted' is always false.