1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_comphelper.hxx" 26 27 #include "opropertybag.hxx" 28 #include "comphelper_module.hxx" 29 30 #include <com/sun/star/beans/PropertyAttribute.hpp> 31 #include <com/sun/star/beans/NamedValue.hpp> 32 #include <com/sun/star/beans/Property.hpp> 33 34 #include <comphelper/namedvaluecollection.hxx> 35 36 #include <cppuhelper/exc_hlp.hxx> 37 #include <osl/thread.h> 38 39 #include <algorithm> 40 #include <functional> 41 42 43 //-------------------------------------------------------------------------- 44 using namespace ::com::sun::star; 45 46 void createRegistryInfo_OPropertyBag() 47 { 48 static ::comphelper::module::OAutoRegistration< ::comphelper::OPropertyBag > aAutoRegistration; 49 } 50 51 //........................................................................ 52 namespace comphelper 53 { 54 //........................................................................ 55 56 using namespace ::com::sun::star::uno; 57 using namespace ::com::sun::star::lang; 58 using namespace ::com::sun::star::beans; 59 using namespace ::com::sun::star::util; 60 using namespace ::com::sun::star::container; 61 62 //==================================================================== 63 //= OPropertyBag 64 //==================================================================== 65 //-------------------------------------------------------------------- 66 OPropertyBag::OPropertyBag( const Reference< XComponentContext >& _rxContext ) 67 :OPropertyBag_PBase( GetBroadcastHelper(), this ) 68 ,::cppu::IEventNotificationHook() 69 ,m_aContext( _rxContext ) 70 ,m_bAutoAddProperties( false ) 71 ,m_NotifyListeners(m_aMutex) 72 ,m_isModified(false) 73 74 { 75 } 76 77 //-------------------------------------------------------------------- 78 OPropertyBag::~OPropertyBag() 79 { 80 } 81 82 //-------------------------------------------------------------------- 83 IMPLEMENT_FORWARD_XINTERFACE2( OPropertyBag, OPropertyBag_Base, OPropertyBag_PBase ) 84 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OPropertyBag, OPropertyBag_Base, OPropertyBag_PBase ) 85 86 //-------------------------------------------------------------------- 87 Sequence< ::rtl::OUString > OPropertyBag::getSupportedServiceNames_static() throw( RuntimeException ) 88 { 89 Sequence< ::rtl::OUString > aServices(1); 90 aServices[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.beans.PropertyBag" ) ); 91 return aServices; 92 } 93 94 //-------------------------------------------------------------------- 95 void SAL_CALL OPropertyBag::initialize( const Sequence< Any >& _rArguments ) throw (Exception, RuntimeException) 96 { 97 ::comphelper::NamedValueCollection aArguments( _rArguments ); 98 99 Sequence< Type > aTypes; 100 if ( aArguments.get_ensureType( "AllowedTypes", aTypes ) ) 101 ::std::copy( 102 aTypes.getConstArray(), 103 aTypes.getConstArray() + aTypes.getLength(), 104 ::std::insert_iterator< TypeBag >( m_aAllowedTypes, m_aAllowedTypes.begin() ) 105 ); 106 107 aArguments.get_ensureType( "AutomaticAddition", m_bAutoAddProperties ); 108 bool AllowEmptyPropertyName(false); 109 aArguments.get_ensureType( "AllowEmptyPropertyName", 110 AllowEmptyPropertyName ); 111 if (AllowEmptyPropertyName) { 112 m_aDynamicProperties.setAllowEmptyPropertyName( 113 AllowEmptyPropertyName); 114 } 115 } 116 117 //-------------------------------------------------------------------- 118 ::rtl::OUString OPropertyBag::getImplementationName_static() throw( RuntimeException ) 119 { 120 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.comphelper.OPropertyBag" ) ); 121 } 122 123 //-------------------------------------------------------------------- 124 Reference< XInterface > SAL_CALL OPropertyBag::Create( const Reference< XComponentContext >& _rxContext ) 125 { 126 return *new OPropertyBag( _rxContext ); 127 } 128 129 //-------------------------------------------------------------------- 130 ::rtl::OUString SAL_CALL OPropertyBag::getImplementationName() throw (RuntimeException) 131 { 132 return getImplementationName_static(); 133 } 134 135 //-------------------------------------------------------------------- 136 ::sal_Bool SAL_CALL OPropertyBag::supportsService( const ::rtl::OUString& rServiceName ) throw (RuntimeException) 137 { 138 Sequence< ::rtl::OUString > aServices( getSupportedServiceNames_static() ); 139 const ::rtl::OUString* pStart = aServices.getConstArray(); 140 const ::rtl::OUString* pEnd = aServices.getConstArray() + aServices.getLength(); 141 return ::std::find( pStart, pEnd, rServiceName ) != pEnd; 142 } 143 144 //-------------------------------------------------------------------- 145 Sequence< ::rtl::OUString > SAL_CALL OPropertyBag::getSupportedServiceNames( ) throw (RuntimeException) 146 { 147 return getSupportedServiceNames_static(); 148 } 149 150 //-------------------------------------------------------------------- 151 void OPropertyBag::fireEvents( 152 sal_Int32 * /*pnHandles*/, 153 sal_Int32 nCount, 154 sal_Bool bVetoable, 155 bool bIgnoreRuntimeExceptionsWhileFiring) 156 { 157 if (nCount && !bVetoable) { 158 setModifiedImpl(sal_True, bIgnoreRuntimeExceptionsWhileFiring); 159 } 160 } 161 162 void OPropertyBag::setModifiedImpl(::sal_Bool bModified, 163 bool bIgnoreRuntimeExceptionsWhileFiring) 164 { 165 { // do not lock mutex while notifying (#i93514#) to prevent deadlock 166 ::osl::MutexGuard aGuard( m_aMutex ); 167 m_isModified = bModified; 168 } 169 if (bModified) { 170 try { 171 Reference<XInterface> xThis(*this); 172 EventObject event(xThis); 173 m_NotifyListeners.notifyEach( 174 &XModifyListener::modified, event); 175 } catch (RuntimeException &) { 176 if (!bIgnoreRuntimeExceptionsWhileFiring) { 177 throw; 178 } 179 } catch (Exception &) { 180 // ignore 181 } 182 } 183 } 184 185 //-------------------------------------------------------------------- 186 ::sal_Bool SAL_CALL OPropertyBag::isModified() 187 throw (RuntimeException) 188 { 189 ::osl::MutexGuard aGuard( m_aMutex ); 190 return m_isModified; 191 } 192 193 void SAL_CALL OPropertyBag::setModified( ::sal_Bool bModified ) 194 throw (PropertyVetoException, RuntimeException) 195 { 196 setModifiedImpl(bModified, false); 197 } 198 199 void SAL_CALL OPropertyBag::addModifyListener( 200 const Reference< XModifyListener > & xListener) 201 throw (RuntimeException) 202 { 203 m_NotifyListeners.addInterface(xListener); 204 } 205 206 void SAL_CALL OPropertyBag::removeModifyListener( 207 const Reference< XModifyListener > & xListener) 208 throw (RuntimeException) 209 { 210 m_NotifyListeners.removeInterface(xListener); 211 } 212 213 //-------------------------------------------------------------------- 214 Reference< XPropertySetInfo > SAL_CALL OPropertyBag::getPropertySetInfo( ) throw(RuntimeException) 215 { 216 return createPropertySetInfo( getInfoHelper() ); 217 } 218 219 //-------------------------------------------------------------------- 220 ::sal_Bool SAL_CALL OPropertyBag::has( const Any& /*aElement*/ ) throw (RuntimeException) 221 { 222 // XSet is only a workaround for addProperty not being able to add default-void properties. 223 // So, everything of XSet except insert is implemented empty 224 return sal_False; 225 } 226 227 //-------------------------------------------------------------------- 228 void SAL_CALL OPropertyBag::insert( const Any& _element ) throw (IllegalArgumentException, ElementExistException, RuntimeException) 229 { 230 // This is a workaround for addProperty not being able to add default-void properties. 231 // If we ever have a smarter XPropertyContainer::addProperty interface, we can remove this, ehm, well, hack. 232 Property aProperty; 233 if ( !( _element >>= aProperty ) ) 234 throw IllegalArgumentException( ::rtl::OUString(), *this, 1 ); 235 236 ::osl::ClearableMutexGuard g( m_aMutex ); 237 238 // check whether the type is allowed, everything else will be checked 239 // by m_aDynamicProperties 240 if ( !m_aAllowedTypes.empty() 241 && m_aAllowedTypes.find( aProperty.Type ) == m_aAllowedTypes.end() 242 ) 243 throw IllegalTypeException( ::rtl::OUString(), *this ); 244 245 m_aDynamicProperties.addVoidProperty( aProperty.Name, aProperty.Type, findFreeHandle(), aProperty.Attributes ); 246 247 // our property info is dirty 248 m_pArrayHelper.reset(); 249 250 g.clear(); 251 setModified(sal_True); 252 } 253 254 //-------------------------------------------------------------------- 255 void SAL_CALL OPropertyBag::remove( const Any& /*aElement*/ ) throw (IllegalArgumentException, NoSuchElementException, RuntimeException) 256 { 257 // XSet is only a workaround for addProperty not being able to add default-void properties. 258 // So, everything of XSet except insert is implemented empty 259 throw NoSuchElementException( ::rtl::OUString(), *this ); 260 } 261 262 263 //-------------------------------------------------------------------- 264 Reference< XEnumeration > SAL_CALL OPropertyBag::createEnumeration( ) throw (RuntimeException) 265 { 266 // XSet is only a workaround for addProperty not being able to add default-void properties. 267 // So, everything of XSet except insert is implemented empty 268 return NULL; 269 } 270 271 //-------------------------------------------------------------------- 272 Type SAL_CALL OPropertyBag::getElementType( ) throw (RuntimeException) 273 { 274 // XSet is only a workaround for addProperty not being able to add default-void properties. 275 // So, everything of XSet except insert is implemented empty 276 return Type(); 277 } 278 279 //-------------------------------------------------------------------- 280 ::sal_Bool SAL_CALL OPropertyBag::hasElements( ) throw (RuntimeException) 281 { 282 // XSet is only a workaround for addProperty not being able to add default-void properties. 283 // So, everything of XSet except insert is implemented empty 284 return sal_False; 285 } 286 287 //-------------------------------------------------------------------- 288 void SAL_CALL OPropertyBag::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const 289 { 290 m_aDynamicProperties.getFastPropertyValue( _nHandle, _rValue ); 291 } 292 293 //-------------------------------------------------------------------- 294 sal_Bool SAL_CALL OPropertyBag::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue ) throw (IllegalArgumentException) 295 { 296 return m_aDynamicProperties.convertFastPropertyValue( _nHandle, _rValue, _rConvertedValue, _rOldValue ); 297 } 298 299 //-------------------------------------------------------------------- 300 void SAL_CALL OPropertyBag::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) throw (Exception) 301 { 302 m_aDynamicProperties.setFastPropertyValue( nHandle, rValue ); 303 } 304 305 //-------------------------------------------------------------------- 306 ::cppu::IPropertyArrayHelper& SAL_CALL OPropertyBag::getInfoHelper() 307 { 308 if ( !m_pArrayHelper.get() ) 309 { 310 Sequence< Property > aProperties; 311 m_aDynamicProperties.describeProperties( aProperties ); 312 m_pArrayHelper.reset( new ::cppu::OPropertyArrayHelper( aProperties ) ); 313 } 314 return *m_pArrayHelper; 315 316 } 317 318 //-------------------------------------------------------------------- 319 sal_Int32 OPropertyBag::findFreeHandle() const 320 { 321 const sal_Int32 nPrime = 1009; 322 const sal_Int32 nSeed = 11; 323 324 sal_Int32 nCheck = nSeed; 325 while ( m_aDynamicProperties.hasPropertyByHandle( nCheck ) && ( nCheck != 1 ) ) 326 { 327 nCheck = ( nCheck * nSeed ) % nPrime; 328 } 329 330 if ( nCheck == 1 ) 331 { // uh ... we already have 1008 handles used up 332 // -> simply count upwards 333 while ( m_aDynamicProperties.hasPropertyByHandle( nCheck ) ) 334 ++nCheck; 335 } 336 337 return nCheck; 338 } 339 340 //-------------------------------------------------------------------- 341 void SAL_CALL OPropertyBag::addProperty( const ::rtl::OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue ) throw (PropertyExistException, IllegalTypeException, IllegalArgumentException, RuntimeException) 342 { 343 ::osl::ClearableMutexGuard g( m_aMutex ); 344 345 // check whether the type is allowed, everything else will be checked 346 // by m_aDynamicProperties 347 Type aPropertyType = _rInitialValue.getValueType(); 348 if ( _rInitialValue.hasValue() 349 && !m_aAllowedTypes.empty() 350 && m_aAllowedTypes.find( aPropertyType ) == m_aAllowedTypes.end() 351 ) 352 throw IllegalTypeException( ::rtl::OUString(), *this ); 353 354 m_aDynamicProperties.addProperty( _rName, findFreeHandle(), _nAttributes, _rInitialValue ); 355 356 // our property info is dirty 357 m_pArrayHelper.reset(); 358 359 g.clear(); 360 setModified(sal_True); 361 } 362 363 //-------------------------------------------------------------------- 364 void SAL_CALL OPropertyBag::removeProperty( const ::rtl::OUString& _rName ) throw (UnknownPropertyException, NotRemoveableException, RuntimeException) 365 { 366 ::osl::ClearableMutexGuard g( m_aMutex ); 367 368 m_aDynamicProperties.removeProperty( _rName ); 369 370 // our property info is dirty 371 m_pArrayHelper.reset(); 372 373 g.clear(); 374 setModified(sal_True); 375 } 376 377 //-------------------------------------------------------------------- 378 namespace 379 { 380 struct ComparePropertyValueByName : public ::std::binary_function< PropertyValue, PropertyValue, bool > 381 { 382 bool operator()( const PropertyValue& _rLHS, const PropertyValue& _rRHS ) 383 { 384 return _rLHS.Name < _rRHS.Name; 385 } 386 }; 387 388 template< typename CLASS > 389 struct TransformPropertyToName : public ::std::unary_function< CLASS, ::rtl::OUString > 390 { 391 const ::rtl::OUString& operator()( const CLASS& _rProp ) 392 { 393 return _rProp.Name; 394 } 395 }; 396 397 struct ExtractPropertyValue : public ::std::unary_function< PropertyValue, Any > 398 { 399 const Any& operator()( const PropertyValue& _rProp ) 400 { 401 return _rProp.Value; 402 } 403 }; 404 } 405 406 //-------------------------------------------------------------------- 407 Sequence< PropertyValue > SAL_CALL OPropertyBag::getPropertyValues( ) throw (RuntimeException) 408 { 409 ::osl::MutexGuard aGuard( m_aMutex ); 410 411 // all registered properties 412 Sequence< Property > aProperties; 413 m_aDynamicProperties.describeProperties( aProperties ); 414 415 // their names 416 Sequence< ::rtl::OUString > aNames( aProperties.getLength() ); 417 ::std::transform( 418 aProperties.getConstArray(), 419 aProperties.getConstArray() + aProperties.getLength(), 420 aNames.getArray(), 421 TransformPropertyToName< Property >() 422 ); 423 424 // their values 425 Sequence< Any > aValues; 426 try 427 { 428 aValues = OPropertyBag_PBase::getPropertyValues( aNames ); 429 if ( aValues.getLength() != aNames.getLength() ) 430 throw RuntimeException(); 431 } 432 catch( const RuntimeException& ) 433 { 434 throw; 435 } 436 catch( const Exception& ) 437 { 438 // ignore 439 } 440 441 // merge names and values, and retrieve the state/handle 442 ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper(); 443 444 Sequence< PropertyValue > aPropertyValues( aNames.getLength() ); 445 const ::rtl::OUString* pName = aNames.getConstArray(); 446 const ::rtl::OUString* pNamesEnd = aNames.getConstArray() + aNames.getLength(); 447 const Any* pValue = aValues.getArray(); 448 PropertyValue* pPropertyValue = aPropertyValues.getArray(); 449 450 for ( ; pName != pNamesEnd; ++pName, ++pValue, ++pPropertyValue ) 451 { 452 pPropertyValue->Name = *pName; 453 pPropertyValue->Handle = rPropInfo.getHandleByName( *pName ); 454 pPropertyValue->Value = *pValue; 455 pPropertyValue->State = getPropertyStateByHandle( pPropertyValue->Handle ); 456 } 457 458 return aPropertyValues; 459 } 460 461 //-------------------------------------------------------------------- 462 void OPropertyBag::impl_setPropertyValues_throw( const Sequence< PropertyValue >& _rProps ) 463 { 464 // sort (the XMultiPropertySet interface requires this) 465 Sequence< PropertyValue > aProperties( _rProps ); 466 ::std::sort( 467 aProperties.getArray(), 468 aProperties.getArray() + aProperties.getLength(), 469 ComparePropertyValueByName() 470 ); 471 472 // a sequence of names 473 Sequence< ::rtl::OUString > aNames( aProperties.getLength() ); 474 ::std::transform( 475 aProperties.getConstArray(), 476 aProperties.getConstArray() + aProperties.getLength(), 477 aNames.getArray(), 478 TransformPropertyToName< PropertyValue >() 479 ); 480 481 try 482 { 483 ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper(); 484 485 // check for unknown properties 486 // we cannot simply rely on the XMultiPropertySet::setPropertyValues 487 // implementation of our base class, since it does not throw 488 // an UnknownPropertyException. More precise, XMultiPropertySet::setPropertyValues 489 // does not allow to throw this exception, while XPropertyAccess::setPropertyValues 490 // requires it 491 sal_Int32 nCount = aNames.getLength(); 492 493 Sequence< sal_Int32 > aHandles( nCount ); 494 sal_Int32* pHandle = aHandles.getArray(); 495 const PropertyValue* pProperty = aProperties.getConstArray(); 496 for ( const ::rtl::OUString* pName = aNames.getConstArray(); 497 pName != aNames.getConstArray() + aNames.getLength(); 498 ++pName, ++pHandle, ++pProperty 499 ) 500 { 501 *pHandle = rPropInfo.getHandleByName( *pName ); 502 if ( *pHandle != -1 ) 503 continue; 504 505 // there's a property requested which we do not know 506 if ( m_bAutoAddProperties ) 507 { 508 // add the property 509 sal_Int16 nAttributes = PropertyAttribute::BOUND | PropertyAttribute::REMOVEABLE | PropertyAttribute::MAYBEDEFAULT; 510 addProperty( *pName, nAttributes, pProperty->Value ); 511 // rPropInfo is invalid, refetch 512 rPropInfo = getInfoHelper(); 513 *pHandle = rPropInfo.getHandleByName( *pName ); 514 continue; 515 } 516 517 // no way out 518 throw UnknownPropertyException( *pName, *this ); 519 } 520 521 // a sequence of values 522 Sequence< Any > aValues( aProperties.getLength() ); 523 ::std::transform( 524 aProperties.getConstArray(), 525 aProperties.getConstArray() + aProperties.getLength(), 526 aValues.getArray(), 527 ExtractPropertyValue() 528 ); 529 530 setFastPropertyValues( nCount, aHandles.getArray(), aValues.getConstArray(), nCount ); 531 } 532 catch( const PropertyVetoException& ) { throw; } 533 catch( const IllegalArgumentException& ) { throw; } 534 catch( const WrappedTargetException& ) { throw; } 535 catch( const RuntimeException& ) { throw; } 536 catch( const UnknownPropertyException& ) { throw; } 537 catch( const Exception& ) 538 { 539 throw WrappedTargetException( ::rtl::OUString(), *this, ::cppu::getCaughtException() ); 540 } 541 } 542 543 //-------------------------------------------------------------------- 544 void SAL_CALL OPropertyBag::setPropertyValues( const Sequence< PropertyValue >& _rProps ) throw (UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, RuntimeException) 545 { 546 ::osl::MutexGuard aGuard( m_aMutex ); 547 impl_setPropertyValues_throw( _rProps ); 548 } 549 550 //-------------------------------------------------------------------- 551 PropertyState OPropertyBag::getPropertyStateByHandle( sal_Int32 _nHandle ) 552 { 553 // for properties which do not support the MAYBEDEFAULT attribute, don't rely on the base class, but 554 // assume they're always in DIRECT state. 555 // (Note that this probably would belong into the base class. However, this would mean we would need 556 // to check all existent usages of the base class, where MAYBEDEFAULT is *not* set, but 557 // a default is nonetheless supplied/used. This is hard to accomplish reliably, in the 558 // current phase. 559 // #i78593# / 2007-07-07 / frank.schoenheit@sun.com 560 561 ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper(); 562 sal_Int16 nAttributes(0); 563 OSL_VERIFY( rPropInfo.fillPropertyMembersByHandle( NULL, &nAttributes, _nHandle ) ); 564 if ( ( nAttributes & PropertyAttribute::MAYBEDEFAULT ) == 0 ) 565 return PropertyState_DIRECT_VALUE; 566 567 return OPropertyBag_PBase::getPropertyStateByHandle( _nHandle ); 568 } 569 570 //-------------------------------------------------------------------- 571 Any OPropertyBag::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const 572 { 573 Any aDefault; 574 m_aDynamicProperties.getPropertyDefaultByHandle( _nHandle, aDefault ); 575 return aDefault; 576 } 577 578 //........................................................................ 579 } // namespace comphelper 580 //........................................................................ 581 582