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