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