/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "sal/config.h" #include "cppuhelper/propertysetmixin.hxx" #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/lang/XComponent.hpp" #include "com/sun/star/lang/XMultiComponentFactory.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/XIdlReflection.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/uno/Any.hxx" #include "com/sun/star/uno/DeploymentException.hpp" #include "com/sun/star/uno/Exception.hpp" #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 "cppuhelper/implbase1.hxx" #include "cppuhelper/weak.hxx" #include "osl/diagnose.h" #include "osl/mutex.hxx" #include "rtl/ref.hxx" #include "rtl/string.h" #include "rtl/ustring.h" #include "rtl/ustring.hxx" #include "sal/types.h" #include "salhelper/simplereferenceobject.hxx" #include #include #include #include #include using cppu::PropertySetMixinImpl; namespace css = com::sun::star; namespace { template< typename T > struct AutoDispose { AutoDispose() {} ~AutoDispose() { try { dispose(); } catch (...) {} } void dispose() { css::uno::Reference< css::lang::XComponent > comp( ifc, css::uno::UNO_QUERY); if (comp.is()) { comp->dispose(); } ifc.clear(); } css::uno::Reference< T > ifc; private: AutoDispose(AutoDispose &); // not defined void operator =(AutoDispose); // not defined }; struct PropertyData { explicit PropertyData( css::beans::Property const & theProperty, bool thePresent): property(theProperty), present(thePresent) {} css::beans::Property property; bool present; }; struct Data: public salhelper::SimpleReferenceObject { typedef std::map< rtl::OUString, PropertyData > PropertyMap; PropertyMap properties; PropertyMap::const_iterator get( css::uno::Reference< css::uno::XInterface > const & object, rtl::OUString const & name) const; protected: void initProperties( css::uno::Reference< css::reflection::XTypeDescription > const & type, css::uno::Sequence< rtl::OUString > const & absentOptional, std::vector< rtl::OUString > * handleNames) { TypeSet seen; initProperties(type, absentOptional, handleNames, &seen); } private: typedef std::set< rtl::OUString > TypeSet; void initProperties( css::uno::Reference< css::reflection::XTypeDescription > const & type, css::uno::Sequence< rtl::OUString > const & absentOptional, std::vector< rtl::OUString > * handleNames, TypeSet * 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, rtl::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< rtl::OUString > const & absentOptional, std::vector< rtl::OUString > * handleNames, TypeSet * seen) { css::uno::Reference< css::reflection::XInterfaceTypeDescription2 > ifc( resolveTypedefs(type), css::uno::UNO_QUERY_THROW); if (seen->insert(ifc->getName()).second) { css::uno::Sequence< css::uno::Reference< css::reflection::XTypeDescription > > bases( ifc->getBaseTypes()); for (sal_Int32 i = 0; i < bases.getLength(); ++i) { initProperties(bases[i], absentOptional, handleNames, seen); } css::uno::Sequence< css::uno::Reference< css::reflection::XInterfaceMemberTypeDescription > > members( ifc->getMembers()); rtl::OUString const * absentBegin = absentOptional.getConstArray(); rtl::OUString const * absentEnd = absentBegin + absentOptional.getLength(); for (sal_Int32 i = 0; i < members.getLength(); ++i) { if (members[i]->getTypeClass() == css::uno::TypeClass_INTERFACE_ATTRIBUTE) { css::uno::Reference< css::reflection::XInterfaceAttributeTypeDescription2 > attr( members[i], css::uno::UNO_QUERY_THROW); sal_Int16 attrAttribs = 0; if (attr->isBound()) { attrAttribs |= css::beans::PropertyAttribute::BOUND; } bool setUnknown = false; if (attr->isReadOnly()) { attrAttribs |= css::beans::PropertyAttribute::READONLY; setUnknown = true; } css::uno::Sequence< css::uno::Reference< css::reflection::XCompoundTypeDescription > > excs( attr->getGetExceptions()); bool getUnknown = false; //XXX Special interpretation of getter/setter exceptions only // works if the specified exceptions are of the exact type, not // of a supertype: for (sal_Int32 j = 0; j < excs.getLength(); ++j) { if ( excs[j]->getName() == "com.sun.star.beans.UnknownPropertyException" ) { getUnknown = true; break; } } excs = attr->getSetExceptions(); for (sal_Int32 j = 0; j < excs.getLength(); ++j) { if ( excs[j]->getName() == "com.sun.star.beans.UnknownPropertyException" ) { setUnknown = true; } else if ( excs[j]->getName() == "com.sun.star.beans.PropertyVetoException" ) { attrAttribs |= css::beans::PropertyAttribute::CONSTRAINED; } } if (getUnknown && setUnknown) { attrAttribs |= css::beans::PropertyAttribute::OPTIONAL; } css::uno::Reference< css::reflection::XTypeDescription > t( attr->getType()); for (;;) { t = resolveTypedefs(t); sal_Int16 n; if (t->getName().matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.beans.Ambiguous<"))) { n = css::beans::PropertyAttribute::MAYBEAMBIGUOUS; } else if (t->getName().matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.beans.Defaulted<"))) { n = css::beans::PropertyAttribute::MAYBEDEFAULT; } else if (t->getName().matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.beans.Optional<"))) { n = css::beans::PropertyAttribute::MAYBEVOID; } else { break; } if ((attrAttribs & n) != 0) { break; } attrAttribs |= n; 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( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "inconsistent UNO type registry")), css::uno::Reference< css::uno::XInterface >()); } t = args[0]; } std::vector< rtl::OUString >::size_type handles = handleNames->size(); if (handles > SAL_MAX_INT32) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "interface type has too many attributes")), css::uno::Reference< css::uno::XInterface >()); } rtl::OUString name(members[i]->getMemberName()); if (!properties.insert( PropertyMap::value_type( name, PropertyData( css::beans::Property( name, static_cast< sal_Int32 >(handles), css::uno::Type( t->getTypeClass(), t->getName()), attrAttribs), (std::find(absentBegin, absentEnd, name) == absentEnd)))). second) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "inconsistent UNO type registry")), css::uno::Reference< css::uno::XInterface >()); } 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::WeakImplHelper1< css::beans::XPropertySetInfo > { public: explicit Info(Data * data): m_data(data) {} virtual css::uno::Sequence< css::beans::Property > SAL_CALL getProperties() throw (css::uno::RuntimeException); virtual css::beans::Property SAL_CALL getPropertyByName( rtl::OUString const & name) throw ( css::beans::UnknownPropertyException, css::uno::RuntimeException); virtual sal_Bool SAL_CALL hasPropertyByName(rtl::OUString const & name) throw (css::uno::RuntimeException); private: rtl::Reference< Data > m_data; }; css::uno::Sequence< css::beans::Property > Info::getProperties() throw (css::uno::RuntimeException) { try { OSL_ASSERT(m_data->properties.size() <= SAL_MAX_INT32); css::uno::Sequence< css::beans::Property > s( static_cast< sal_Int32 >(m_data->properties.size())); sal_Int32 n = 0; for (Data::PropertyMap::iterator i(m_data->properties.begin()); i != m_data->properties.end(); ++i) { if (i->second.present) { s[n++] = i->second.property; } } s.realloc(n); return s; } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< cppu::OWeakObject * >(this)); } } css::beans::Property Info::getPropertyByName(rtl::OUString const & name) throw (css::beans::UnknownPropertyException, css::uno::RuntimeException) { return m_data->get(static_cast< cppu::OWeakObject * >(this), name)-> second.property; } sal_Bool Info::hasPropertyByName(rtl::OUString const & name) throw (css::uno::RuntimeException) { 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 (BoundListenerBag::const_iterator i(m_impl->specificListeners.begin()); i != m_impl->specificListeners.end(); ++i) { try { (*i)->propertyChange(m_impl->event); } catch (css::lang::DisposedException &) {} } for (BoundListenerBag::const_iterator i( m_impl->unspecificListeners.begin()); i != m_impl->unspecificListeners.end(); ++i) { try { (*i)->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< rtl::OUString > const & absentOptional, css::uno::Type const & type); rtl::OUString translateHandle( css::uno::Reference< css::uno::XInterface > const & object, sal_Int32 handle) const; void setProperty( css::uno::Reference< css::uno::XInterface > const & object, rtl::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, rtl::OUString const & name, css::beans::PropertyState * state) const; PropertySetMixinImpl::Implements implements; css::uno::Sequence< rtl::OUString > handleMap; typedef std::map< rtl::OUString, BoundListenerBag > BoundListenerMap; typedef std::multiset< css::uno::Reference< css::beans::XVetoableChangeListener > > VetoListenerBag; typedef std::map< rtl::OUString, VetoListenerBag > VetoListenerMap; mutable osl::Mutex mutex; BoundListenerMap boundListeners; VetoListenerMap vetoListeners; bool disposed; private: css::uno::Reference< css::reflection::XIdlClass > getReflection( rtl::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::Sequence< rtl::OUString > m_absentOptional; 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< rtl::OUString > const & absentOptional, css::uno::Type const & type): implements(theImplements), disposed(false), m_context(context), m_absentOptional(absentOptional), m_type(type) { OSL_ASSERT( context.is() && ((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 = css::uno::Reference< css::reflection::XTypeDescription >( css::uno::Reference< css::container::XHierarchicalNameAccess >( m_context->getValueByName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/singletons/com.sun.star.reflection." "theTypeDescriptionManager"))), css::uno::UNO_QUERY_THROW)->getByHierarchicalName( m_type.getTypeName()), css::uno::UNO_QUERY_THROW); } catch (css::container::NoSuchElementException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected" " com.sun.star.container.NoSuchElementException: ")) + e.Message), css::uno::Reference< css::uno::XInterface >()); } std::vector< rtl::OUString > handleNames; initProperties(ifc, m_absentOptional, &handleNames); std::vector< rtl::OUString >::size_type size = handleNames.size(); OSL_ASSERT(size <= SAL_MAX_INT32); handleMap.realloc(static_cast< sal_Int32 >(size)); std::copy(handleNames.begin(), handleNames.end(), handleMap.getArray()); } rtl::OUString 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( (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad handle ")) + rtl::OUString::valueOf(handle)), object); } return handleMap[handle]; } void PropertySetMixinImpl::Impl::setProperty( css::uno::Reference< css::uno::XInterface > const & object, rtl::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( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "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 { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected" " com.sun.star.lang.IllegalArgumentException: ")) + e.Message), object); } } 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( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("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( getCppuType( static_cast< css::beans::UnknownPropertyException * >(0))) && ((i->second.property.Attributes & css::beans::PropertyAttribute::OPTIONAL) != 0)) { throw css::beans::UnknownPropertyException(name, object); } else if (e.TargetException.isExtractableTo( getCppuType( static_cast< css::beans::PropertyVetoException * >( 0))) && ((i->second.property.Attributes & css::beans::PropertyAttribute::CONSTRAINED) != 0)) { throw css::beans::PropertyVetoException(name, object); } else { throw css::lang::WrappedTargetException( e.Message, object, e.TargetException); } } } css::uno::Any PropertySetMixinImpl::Impl::getProperty( css::uno::Reference< css::uno::XInterface > const & object, rtl::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) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected com.sun.star.lang.IllegalArgumentException: ")) + e.Message), 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( getCppuType( static_cast< css::beans::UnknownPropertyException * >(0))) && ((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().matchAsciiL( RTL_CONSTASCII_STRINGPARAM("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( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("IsAmbiguous"))), css::uno::UNO_QUERY_THROW)->get(value) >>= isAmbiguous)) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected type of" " com.sun.star.beans.Ambiguous IsAmbiguous" " member")), object); } value = css::uno::Reference< css::reflection::XIdlField2 >( ambiguous->getField( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Value"))), css::uno::UNO_QUERY_THROW)->get(value); } catch (css::lang::IllegalArgumentException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected com.sun.star.lang." "IllegalArgumentException: ")) + e.Message), object); } undoAmbiguous = false; } else if (undoDefaulted && value.getValueTypeName().matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "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( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("IsDefaulted"))), css::uno::UNO_QUERY_THROW)->get(value) >>= isDefaulted)) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected type of" " com.sun.star.beans.Defaulted IsDefaulted" " member")), object); } value = css::uno::Reference< css::reflection::XIdlField2 >( defaulted->getField( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Value"))), css::uno::UNO_QUERY_THROW)->get(value); } catch (css::lang::IllegalArgumentException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected com.sun.star.lang." "IllegalArgumentException: ")) + e.Message), object); } undoDefaulted = false; } else if (undoOptional && value.getValueTypeName().matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "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( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("IsPresent"))), css::uno::UNO_QUERY_THROW)->get(value) >>= present)) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected type of com.sun.star.beans.Optional" " IsPresent member")), object); } if (!present) { value.clear(); break; } value = css::uno::Reference< css::reflection::XIdlField2 >( optional->getField( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Value"))), css::uno::UNO_QUERY_THROW)->get(value); } catch (css::lang::IllegalArgumentException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected com.sun.star.lang." "IllegalArgumentException: ")) + e.Message), object); } undoOptional = false; } else { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected type of attribute ")) + name), object); } } if (state != 0) { //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(rtl::OUString const & typeName) const { css::uno::Reference< css::lang::XMultiComponentFactory > factory( m_context->getServiceManager(), css::uno::UNO_QUERY_THROW); AutoDispose< css::reflection::XIdlReflection > refl; try { refl.ifc = css::uno::Reference< css::reflection::XIdlReflection >( factory->createInstanceWithContext( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.reflection.CoreReflection")), m_context), css::uno::UNO_QUERY_THROW); } catch (css::uno::RuntimeException &) { throw; } catch (css::uno::Exception & e) { throw css::uno::DeploymentException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "component context fails to supply service" " com.sun.star.reflection.CoreReflection: ")) + e.Message), m_context); } css::uno::Reference< css::reflection::XIdlClass > idlClass( refl.ifc->forName(typeName), css::uno::UNO_QUERY_THROW); refl.dispose(); return idlClass; } 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) { OSL_ASSERT( (wrapAmbiguous || !isAmbiguous) && (wrapDefaulted || !isDefaulted)); if (wrapAmbiguous && type->getName().matchAsciiL( RTL_CONSTASCII_STRINGPARAM("com.sun.star.beans.Ambiguous<"))) { css::uno::Any strct; type->createObject(strct); try { css::uno::Reference< css::reflection::XIdlField2 > field( type->getField( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Value"))), 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( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("IsAmbiguous"))), css::uno::UNO_QUERY_THROW)->set( strct, css::uno::makeAny(isAmbiguous)); } catch (css::lang::IllegalArgumentException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected" " com.sun.star.lang.IllegalArgumentException: ")) + e.Message), object); } catch (css::lang::IllegalAccessException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected" " com.sun.star.lang.IllegalAccessException: ")) + e.Message), object); } return strct; } else if (wrapDefaulted && type->getName().matchAsciiL( RTL_CONSTASCII_STRINGPARAM("com.sun.star.beans.Defaulted<"))) { css::uno::Any strct; type->createObject(strct); try { css::uno::Reference< css::reflection::XIdlField2 > field( type->getField( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Value"))), 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( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("IsDefaulted"))), css::uno::UNO_QUERY_THROW)->set( strct, css::uno::makeAny(isDefaulted)); } catch (css::lang::IllegalArgumentException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected" " com.sun.star.lang.IllegalArgumentException: ")) + e.Message), object); } catch (css::lang::IllegalAccessException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected" " com.sun.star.lang.IllegalAccessException: ")) + e.Message), object); } return strct; } else if (wrapOptional && type->getName().matchAsciiL( RTL_CONSTASCII_STRINGPARAM("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( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("IsPresent"))), css::uno::UNO_QUERY_THROW)->set( strct, css::uno::makeAny(present)); if (present) { css::uno::Reference< css::reflection::XIdlField2 > field( type->getField( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Value"))), css::uno::UNO_QUERY_THROW); field->set( strct, wrapValue( object, value, field->getType(), wrapAmbiguous, isAmbiguous, wrapDefaulted, isDefaulted, false)); } } catch (css::lang::IllegalArgumentException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected" " com.sun.star.lang.IllegalArgumentException: ")) + e.Message), object); } catch (css::lang::IllegalAccessException & e) { throw css::uno::RuntimeException( (rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected" " com.sun.star.lang.IllegalAccessException: ")) + e.Message), object); } return strct; } else { if (wrapAmbiguous || wrapDefaulted || wrapOptional) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unexpected type of attribute")), object); } return value; } } PropertySetMixinImpl::PropertySetMixinImpl( css::uno::Reference< css::uno::XComponentContext > const & context, Implements implements, css::uno::Sequence< rtl::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(rtl::OUString const & propertyName) { if (!propertyName.isEmpty()) { m_impl->get( static_cast< css::beans::XPropertySet * >(this), propertyName); } } void PropertySetMixinImpl::prepareSet( rtl::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)); OSL_ASSERT(it != m_impl->properties.end()); Impl::VetoListenerBag specificVeto; Impl::VetoListenerBag unspecificVeto; { osl::MutexGuard g(m_impl->mutex); if (m_impl->disposed) { throw css::lang::DisposedException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("disposed")), 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(rtl::OUString()); if (i != m_impl->vetoListeners.end()) { unspecificVeto = i->second; } } if ((it->second.property.Attributes & css::beans::PropertyAttribute::BOUND) != 0) { OSL_ASSERT(boundListeners != 0); 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(rtl::OUString()); 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 (Impl::VetoListenerBag::iterator i(specificVeto.begin()); i != specificVeto.end(); ++i) { try { (*i)->vetoableChange(event); } catch (css::lang::DisposedException &) {} } for (Impl::VetoListenerBag::iterator i(unspecificVeto.begin()); i != unspecificVeto.end(); ++i) { try { (*i)->vetoableChange(event); } catch (css::lang::DisposedException &) {} } } if ((it->second.property.Attributes & css::beans::PropertyAttribute::BOUND) != 0) { OSL_ASSERT(boundListeners != 0); 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; { osl::MutexGuard 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 (Impl::BoundListenerMap::iterator i(boundListeners.begin()); i != boundListeners.end(); ++i) { for (BoundListenerBag::iterator j(i->second.begin()); j != i->second.end(); ++j) { (*j)->disposing(event); } } for (Impl::VetoListenerMap::iterator i(vetoListeners.begin()); i != vetoListeners.end(); ++i) { for (Impl::VetoListenerBag::iterator j(i->second.begin()); j != i->second.end(); ++j) { (*j)->disposing(event); } } } css::uno::Any PropertySetMixinImpl::queryInterface(css::uno::Type const & type) throw (css::uno::RuntimeException) { if (((m_impl->implements & IMPLEMENTS_PROPERTY_SET) != 0 && type == css::beans::XPropertySet::static_type())) { css::uno::Reference< css::uno::XInterface > ifc( static_cast< css::beans::XPropertySet * >(this)); return css::uno::Any(&ifc, type); } else if ((m_impl->implements & IMPLEMENTS_FAST_PROPERTY_SET) != 0 && type == css::beans::XFastPropertySet::static_type()) { css::uno::Reference< css::uno::XInterface > ifc( static_cast< css::beans::XFastPropertySet * >(this)); return css::uno::Any(&ifc, type); } else if ((m_impl->implements & IMPLEMENTS_PROPERTY_ACCESS) != 0 && type == css::beans::XPropertyAccess::static_type()) { css::uno::Reference< css::uno::XInterface > ifc( static_cast< css::beans::XPropertyAccess * >(this)); return css::uno::Any(&ifc, type); } else { return css::uno::Any(); } } css::uno::Reference< css::beans::XPropertySetInfo > PropertySetMixinImpl::getPropertySetInfo() throw (css::uno::RuntimeException) { try { return new Info(m_impl); } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } void PropertySetMixinImpl::setPropertyValue( rtl::OUString const & propertyName, css::uno::Any const & value) throw ( css::beans::UnknownPropertyException, css::beans::PropertyVetoException, css::lang::IllegalArgumentException, css::lang::WrappedTargetException, css::uno::RuntimeException) { try { m_impl->setProperty( static_cast< css::beans::XPropertySet * >(this), propertyName, value, false, false, 1); } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } css::uno::Any PropertySetMixinImpl::getPropertyValue( rtl::OUString const & propertyName) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { try { return m_impl->getProperty( static_cast< css::beans::XPropertySet * >(this), propertyName, 0); } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } void PropertySetMixinImpl::addPropertyChangeListener( rtl::OUString const & propertyName, css::uno::Reference< css::beans::XPropertyChangeListener > const & listener) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { css::uno::Reference< css::beans::XPropertyChangeListener >( listener, css::uno::UNO_QUERY_THROW); // reject NULL listener checkUnknown(propertyName); try { bool disposed; { osl::MutexGuard 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))); } } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } void PropertySetMixinImpl::removePropertyChangeListener( rtl::OUString const & propertyName, css::uno::Reference< css::beans::XPropertyChangeListener > const & listener) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(listener.is()); checkUnknown(propertyName); try { osl::MutexGuard 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); } } } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } void PropertySetMixinImpl::addVetoableChangeListener( rtl::OUString const & propertyName, css::uno::Reference< css::beans::XVetoableChangeListener > const & listener) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { css::uno::Reference< css::beans::XVetoableChangeListener >( listener, css::uno::UNO_QUERY_THROW); // reject NULL listener checkUnknown(propertyName); try { bool disposed; { osl::MutexGuard 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))); } } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } void PropertySetMixinImpl::removeVetoableChangeListener( rtl::OUString const & propertyName, css::uno::Reference< css::beans::XVetoableChangeListener > const & listener) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(listener.is()); checkUnknown(propertyName); try { osl::MutexGuard 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); } } } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } void PropertySetMixinImpl::setFastPropertyValue( sal_Int32 handle, css::uno::Any const & value) throw ( css::beans::UnknownPropertyException, css::beans::PropertyVetoException, css::lang::IllegalArgumentException, css::lang::WrappedTargetException, css::uno::RuntimeException) { try { m_impl->setProperty( static_cast< css::beans::XPropertySet * >(this), m_impl->translateHandle( static_cast< css::beans::XPropertySet * >(this), handle), value, false, false, 1); } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } css::uno::Any PropertySetMixinImpl::getFastPropertyValue(sal_Int32 handle) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { try { return m_impl->getProperty( static_cast< css::beans::XPropertySet * >(this), m_impl->translateHandle( static_cast< css::beans::XPropertySet * >(this), handle), 0); } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } css::uno::Sequence< css::beans::PropertyValue > PropertySetMixinImpl::getPropertyValues() throw (css::uno::RuntimeException) { try { css::uno::Sequence< css::beans::PropertyValue > s( m_impl->handleMap.getLength()); sal_Int32 n = 0; for (sal_Int32 i = 0; i < m_impl->handleMap.getLength(); ++i) { try { s[n].Value = m_impl->getProperty( static_cast< css::beans::XPropertySet * >(this), m_impl->handleMap[i], &s[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); } s[n].Name = m_impl->handleMap[i]; s[n].Handle = i; ++n; } s.realloc(n); return s; } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } void PropertySetMixinImpl::setPropertyValues( css::uno::Sequence< css::beans::PropertyValue > const & props) throw ( css::beans::UnknownPropertyException, css::beans::PropertyVetoException, css::lang::IllegalArgumentException, css::lang::WrappedTargetException, css::uno::RuntimeException) { try { for (sal_Int32 i = 0; i < props.getLength(); ++i) { if (props[i].Handle != -1 && (props[i].Name != m_impl->translateHandle( static_cast< css::beans::XPropertySet * >(this), props[i].Handle))) { throw css::beans::UnknownPropertyException( (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("name ")) + props[i].Name + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(" does not match handle ")) + rtl::OUString::valueOf(props[i].Handle)), static_cast< css::beans::XPropertySet * >(this)); } m_impl->setProperty( static_cast< css::beans::XPropertySet * >(this), props[i].Name, props[i].Value, props[i].State == css::beans::PropertyState_AMBIGUOUS_VALUE, props[i].State == css::beans::PropertyState_DEFAULT_VALUE, 0); } } catch (std::bad_alloc &) { //TODO OutOfMemoryException: throw css::uno::RuntimeException( rtl::OUString(), static_cast< css::beans::XPropertySet * >(this)); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */