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