xref: /trunk/main/comphelper/source/property/opropertybag.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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 
createRegistryInfo_OPropertyBag()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     //--------------------------------------------------------------------
OPropertyBag(const Reference<XComponentContext> & _rxContext)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     //--------------------------------------------------------------------
~OPropertyBag()81     OPropertyBag::~OPropertyBag()
82     {
83     }
84 
85     //--------------------------------------------------------------------
IMPLEMENT_FORWARD_XINTERFACE2(OPropertyBag,OPropertyBag_Base,OPropertyBag_PBase)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     //--------------------------------------------------------------------
initialize(const Sequence<Any> & _rArguments)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     //--------------------------------------------------------------------
getImplementationName_static()121     ::rtl::OUString OPropertyBag::getImplementationName_static()
122     {
123         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.comphelper.OPropertyBag" ) );
124     }
125 
126     //--------------------------------------------------------------------
Create(const Reference<XComponentContext> & _rxContext)127     Reference< XInterface > SAL_CALL OPropertyBag::Create( const Reference< XComponentContext >& _rxContext )
128     {
129         return *new OPropertyBag( _rxContext );
130     }
131 
132     //--------------------------------------------------------------------
getImplementationName()133     ::rtl::OUString SAL_CALL OPropertyBag::getImplementationName()
134     {
135         return getImplementationName_static();
136     }
137 
138     //--------------------------------------------------------------------
supportsService(const::rtl::OUString & rServiceName)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     //--------------------------------------------------------------------
getSupportedServiceNames()148     Sequence< ::rtl::OUString > SAL_CALL OPropertyBag::getSupportedServiceNames(  )
149     {
150         return getSupportedServiceNames_static();
151     }
152 
153     //--------------------------------------------------------------------
fireEvents(sal_Int32 *,sal_Int32 nCount,sal_Bool bVetoable,bool bIgnoreRuntimeExceptionsWhileFiring)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 
setModifiedImpl(::sal_Bool bModified,bool bIgnoreRuntimeExceptionsWhileFiring)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     //--------------------------------------------------------------------
isModified()189     ::sal_Bool SAL_CALL OPropertyBag::isModified()
190     {
191         ::osl::MutexGuard aGuard( m_aMutex );
192         return m_isModified;
193     }
194 
setModified(::sal_Bool bModified)195     void SAL_CALL OPropertyBag::setModified( ::sal_Bool bModified )
196     {
197         setModifiedImpl(bModified, false);
198     }
199 
addModifyListener(const Reference<XModifyListener> & xListener)200     void SAL_CALL OPropertyBag::addModifyListener(
201         const Reference< XModifyListener > & xListener)
202     {
203         m_NotifyListeners.addInterface(xListener);
204     }
205 
removeModifyListener(const Reference<XModifyListener> & xListener)206     void SAL_CALL OPropertyBag::removeModifyListener(
207         const Reference< XModifyListener > & xListener)
208     {
209         m_NotifyListeners.removeInterface(xListener);
210     }
211 
212     //--------------------------------------------------------------------
getPropertySetInfo()213     Reference< XPropertySetInfo > SAL_CALL OPropertyBag::getPropertySetInfo(  )
214     {
215         return createPropertySetInfo( getInfoHelper() );
216     }
217 
218     //--------------------------------------------------------------------
has(const Any &)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     //--------------------------------------------------------------------
insert(const Any & _element)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     //--------------------------------------------------------------------
remove(const Any &)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     //--------------------------------------------------------------------
createEnumeration()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     //--------------------------------------------------------------------
getElementType()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     //--------------------------------------------------------------------
hasElements()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     //--------------------------------------------------------------------
getFastPropertyValue(Any & _rValue,sal_Int32 _nHandle) const287     void SAL_CALL OPropertyBag::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
288     {
289         m_aDynamicProperties.getFastPropertyValue( _nHandle, _rValue );
290     }
291 
292     //--------------------------------------------------------------------
convertFastPropertyValue(Any & _rConvertedValue,Any & _rOldValue,sal_Int32 _nHandle,const Any & _rValue)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     //--------------------------------------------------------------------
setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any & rValue)299     void SAL_CALL OPropertyBag::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
300     {
301         m_aDynamicProperties.setFastPropertyValue( nHandle, rValue );
302     }
303 
304     //--------------------------------------------------------------------
getInfoHelper()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     //--------------------------------------------------------------------
findFreeHandle() const318     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     //--------------------------------------------------------------------
addProperty(const::rtl::OUString & _rName,::sal_Int16 _nAttributes,const Any & _rInitialValue)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     //--------------------------------------------------------------------
removeProperty(const::rtl::OUString & _rName)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         {
operator ()comphelper::__anonb5b249870111::ComparePropertyValueByName381             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         {
operator ()comphelper::__anonb5b249870111::TransformPropertyToName390             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         {
operator ()comphelper::__anonb5b249870111::ExtractPropertyValue398             const Any& operator()( const PropertyValue& _rProp )
399             {
400                 return _rProp.Value;
401             }
402         };
403     }
404 
405     //--------------------------------------------------------------------
getPropertyValues()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     //--------------------------------------------------------------------
impl_setPropertyValues_throw(const Sequence<PropertyValue> & _rProps)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     //--------------------------------------------------------------------
setPropertyValues(const Sequence<PropertyValue> & _rProps)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     //--------------------------------------------------------------------
getPropertyStateByHandle(sal_Int32 _nHandle)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     //--------------------------------------------------------------------
getPropertyDefaultByHandle(sal_Int32 _nHandle) const570     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