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_forms.hxx"
26 #include "propertybaghelper.hxx"
27 
28 #include "property.hxx"
29 
30 /** === begin UNO includes === **/
31 #include <com/sun/star/lang/DisposedException.hpp>
32 #include <com/sun/star/beans/PropertyExistException.hpp>
33 #include <com/sun/star/beans/XMultiPropertySet.hpp>
34 #include <com/sun/star/beans/NotRemoveableException.hpp>
35 #include <com/sun/star/beans/UnknownPropertyException.hpp>
36 /** === end UNO includes === **/
37 
38 #include <tools/diagnose_ex.h>
39 
40 #include <comphelper/sequence.hxx>
41 #include <rtl/logfile.hxx>
42 #include "rtl/instance.hxx"
43 
44 
45 #define NEW_HANDLE_BASE 10000
46 
47 //........................................................................
48 namespace frm
49 {
50 //........................................................................
51 
52 	/** === begin UNO using === **/
53     using ::com::sun::star::lang::DisposedException;
54     using ::com::sun::star::uno::Sequence;
55     using ::com::sun::star::beans::Property;
56     using ::com::sun::star::uno::Any;
57     using ::com::sun::star::beans::PropertyExistException;
58     using ::com::sun::star::beans::PropertyValue;
59     using ::com::sun::star::uno::Reference;
60     using ::com::sun::star::uno::UNO_QUERY_THROW;
61     using ::com::sun::star::beans::XMultiPropertySet;
62     using ::com::sun::star::beans::XPropertySetInfo;
63     using ::com::sun::star::uno::RuntimeException;
64     using ::com::sun::star::uno::Exception;
65     using ::com::sun::star::beans::NotRemoveableException;
66     using ::com::sun::star::beans::UnknownPropertyException;
67 	/** === end UNO using === **/
68     namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
69 
70 	//====================================================================
71 	//= helper
72 	//====================================================================
73     namespace
74     {
75     	//----------------------------------------------------------------
lcl_getPropertyInfos()76         static ::comphelper::IPropertyInfoService& lcl_getPropertyInfos()
77         {
78 	        static ConcreteInfoService s_aPropInfos;
79             return s_aPropInfos;
80         }
81     }
82 
83 	//====================================================================
84 	//= PropertyBagHelper
85 	//====================================================================
86 	//--------------------------------------------------------------------
PropertyBagHelper(IPropertyBagHelperContext & _rContext)87     PropertyBagHelper::PropertyBagHelper( IPropertyBagHelperContext& _rContext )
88         :m_rContext( _rContext )
89         ,m_pPropertyArrayHelper( NULL )
90         ,m_bDisposed( false )
91     {
92         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::PropertyBagHelper" );
93     }
94 
95 	//--------------------------------------------------------------------
~PropertyBagHelper()96     PropertyBagHelper::~PropertyBagHelper()
97     {
98         delete m_pPropertyArrayHelper, m_pPropertyArrayHelper = NULL;
99     }
100 
101     //--------------------------------------------------------------------
dispose()102     void PropertyBagHelper::dispose()
103     {
104         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::dispose" );
105         m_bDisposed = true;
106     }
107 
108     //--------------------------------------------------------------------
impl_nts_checkDisposed_throw() const109     void PropertyBagHelper::impl_nts_checkDisposed_throw() const
110     {
111         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::impl_nts_checkDisposed_throw" );
112         if ( m_bDisposed )
113             throw DisposedException();
114     }
115 
116     //--------------------------------------------------------------------
impl_nts_invalidatePropertySetInfo()117     void PropertyBagHelper::impl_nts_invalidatePropertySetInfo()
118     {
119         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::impl_nts_invalidatePropertySetInfo" );
120         delete m_pPropertyArrayHelper, m_pPropertyArrayHelper = NULL;
121     }
122 
123     //--------------------------------------------------------------------
impl_findFreeHandle(const::rtl::OUString & _rPropertyName)124     sal_Int32 PropertyBagHelper::impl_findFreeHandle( const ::rtl::OUString& _rPropertyName )
125     {
126         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::impl_findFreeHandle" );
127         ::comphelper::OPropertyArrayAggregationHelper& rPropInfo( impl_ts_getArrayHelper() );
128 
129         // check the preferred handle
130         sal_Int32 nHandle = lcl_getPropertyInfos().getPreferedPropertyId( _rPropertyName );
131         if ( ( nHandle != -1 ) && rPropInfo.fillPropertyMembersByHandle( NULL, NULL, nHandle ) )
132             nHandle = -1;
133 
134         // seach a free handle in <math>F_1009</math>
135         if ( nHandle == -1 )
136         {
137             sal_Int32 nPrime = 1009;
138             sal_Int32 nFactor = 11;
139             sal_Int32 nNum = nFactor;
140             while ( nNum != 1 )
141             {
142                 if ( !rPropInfo.fillPropertyMembersByHandle( NULL, NULL, nNum + NEW_HANDLE_BASE ) )
143                 {
144                     // handle not used, yet
145                     nHandle = nNum + NEW_HANDLE_BASE;
146                     break;
147                 }
148                 nNum = ( nNum * nFactor ) % nPrime;
149             }
150         }
151 
152         // search a free handle greater NEW_HANDLE_BASE
153         if ( nHandle == -1 )
154         {
155             nHandle = NEW_HANDLE_BASE + 1009;
156             while ( rPropInfo.fillPropertyMembersByHandle( NULL, NULL, nHandle ) )
157                 ++nHandle;
158         }
159 
160         return nHandle;
161     }
162 
163     //--------------------------------------------------------------------
impl_ts_getArrayHelper() const164     ::comphelper::OPropertyArrayAggregationHelper& PropertyBagHelper::impl_ts_getArrayHelper() const
165     {
166         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::impl_ts_getArrayHelper" );
167         //::osl::MutexGuard aGuard( m_rContext.getMutex() );
168         OPropertyArrayAggregationHelper* p = m_pPropertyArrayHelper;
169         if ( !p )
170         {
171             ::osl::MutexGuard aGuard( m_rContext.getMutex() );
172             p = m_pPropertyArrayHelper;
173             if ( !p )
174             {
175                 // our own fixed and our aggregate's properties
176 	            Sequence< Property > aFixedProps;
177 	            Sequence< Property > aAggregateProps;
178                 m_rContext.describeFixedAndAggregateProperties( aFixedProps, aAggregateProps );
179 
180                 // our dynamic properties
181                 Sequence< Property > aDynamicProps;
182                 m_aDynamicProperties.describeProperties( aDynamicProps );
183 
184                 Sequence< Property > aOwnProps(
185                     ::comphelper::concatSequences( aFixedProps, aDynamicProps ) );
186 
187                 p = new OPropertyArrayAggregationHelper( aOwnProps, aAggregateProps, &lcl_getPropertyInfos(), NEW_HANDLE_BASE );
188                 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
189                 const_cast< PropertyBagHelper* >( this )->m_pPropertyArrayHelper = p;
190             }
191         } // if ( !p )
192         else
193         {
194             OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
195         }
196         return *p;
197     }
198 
199     //--------------------------------------------------------------------
addProperty(const::rtl::OUString & _rName,::sal_Int16 _nAttributes,const Any & _rInitialValue)200     void PropertyBagHelper::addProperty( const ::rtl::OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
201     {
202         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::addProperty" );
203         ::osl::MutexGuard aGuard( m_rContext.getMutex() );
204         impl_nts_checkDisposed_throw();
205 
206         //----------------------------------------------
207         // check name sanity
208         ::comphelper::OPropertyArrayAggregationHelper& aPropInfo( impl_ts_getArrayHelper() );
209         if ( aPropInfo.hasPropertyByName( _rName ) )
210             throw PropertyExistException( _rName, m_rContext.getPropertiesInterface() );
211 
212         //----------------------------------------------
213         // normalize the REMOVEABLE attribute - the FormComponent service
214         // requires that all dynamic properties are REMOVEABLE
215         _nAttributes |= PropertyAttribute::REMOVEABLE;
216 
217         //----------------------------------------------
218         // find a free handle
219         sal_Int32 nHandle = impl_findFreeHandle( _rName );
220 
221         //----------------------------------------------
222         // register the property, and invalidate our property meta data
223         m_aDynamicProperties.addProperty( _rName, nHandle, _nAttributes, _rInitialValue );
224         impl_nts_invalidatePropertySetInfo();
225     }
226 
227     //--------------------------------------------------------------------
removeProperty(const::rtl::OUString & _rName)228     void PropertyBagHelper::removeProperty( const ::rtl::OUString& _rName )
229     {
230         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::removeProperty" );
231         ::osl::MutexGuard aGuard( m_rContext.getMutex() );
232         impl_nts_checkDisposed_throw();
233 
234         // check whether it's removeable at all
235         Reference< XMultiPropertySet > xMe( m_rContext.getPropertiesInterface(), UNO_QUERY_THROW );
236         Reference< XPropertySetInfo > xPSI( xMe->getPropertySetInfo(), UNO_QUERY_THROW );
237         Property aProperty( xPSI->getPropertyByName( _rName ) );
238         if ( ( aProperty.Attributes & PropertyAttribute::REMOVEABLE ) == 0 )
239             throw NotRemoveableException( _rName, xMe );
240 
241         m_aDynamicProperties.removeProperty( _rName );
242         impl_nts_invalidatePropertySetInfo();
243     }
244 
245     //--------------------------------------------------------------------
246     namespace
247     {
248         //----------------------------------------------------------------
249         struct SelectNameOfProperty : public ::std::unary_function< Property, ::rtl::OUString >
250         {
operator ()frm::__anon2ecdf6e70211::SelectNameOfProperty251             const ::rtl::OUString& operator()( const Property& _rProp ) const { return _rProp.Name; }
252         };
253 
254         //----------------------------------------------------------------
255         struct SelectNameOfPropertyValue : public ::std::unary_function< PropertyValue, ::rtl::OUString >
256         {
operator ()frm::__anon2ecdf6e70211::SelectNameOfPropertyValue257             const ::rtl::OUString& operator()( const PropertyValue& _rProp ) const { return _rProp.Name; }
258         };
259 
260         //----------------------------------------------------------------
261         struct SelectValueOfPropertyValue : public ::std::unary_function< PropertyValue, Any >
262         {
operator ()frm::__anon2ecdf6e70211::SelectValueOfPropertyValue263             const Any& operator()( const PropertyValue& _rProp ) const { return _rProp.Value; }
264         };
265 
266         //----------------------------------------------------------------
267         struct PropertyValueLessByName : public ::std::binary_function< PropertyValue, PropertyValue, bool >
268         {
operator ()frm::__anon2ecdf6e70211::PropertyValueLessByName269             bool operator()( const PropertyValue& _lhs, const PropertyValue _rhs ) const
270             {
271                 return _lhs.Name < _rhs.Name;
272             }
273         };
274     }
275 
276     //--------------------------------------------------------------------
getPropertyValues()277     Sequence< PropertyValue > PropertyBagHelper::getPropertyValues()
278     {
279         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::getPropertyValues" );
280         ::osl::MutexGuard aGuard( m_rContext.getMutex() );
281         impl_nts_checkDisposed_throw();
282 
283         Reference< XMultiPropertySet > xMe( m_rContext.getPropertiesInterface(), UNO_QUERY_THROW );
284         Reference< XPropertySetInfo > xPSI( xMe->getPropertySetInfo(), UNO_QUERY_THROW );
285 
286         Sequence< Property > aProperties( xPSI->getProperties() );
287         Sequence< ::rtl::OUString > aPropertyNames( aProperties.getLength() );
288         ::std::transform( aProperties.getConstArray(), aProperties.getConstArray() + aProperties.getLength(),
289             aPropertyNames.getArray(), SelectNameOfProperty() );
290 
291         Sequence< Any > aValues;
292         try
293         {
294             aValues = xMe->getPropertyValues( aPropertyNames );
295 
296             if ( aValues.getLength() != aPropertyNames.getLength() )
297                 throw RuntimeException();
298         }
299         catch( const RuntimeException& ) { throw; }
300         catch( const Exception& )
301         {
302     	    DBG_UNHANDLED_EXCEPTION();
303         }
304         Sequence< PropertyValue > aPropertyValues( aValues.getLength() );
305         PropertyValue* pPropertyValue = aPropertyValues.getArray();
306 
307         const ::rtl::OUString* pName = aPropertyNames.getConstArray();
308         const ::rtl::OUString* pNameEnd = aPropertyNames.getConstArray() + aPropertyNames.getLength();
309         const Any* pValue = aValues.getConstArray();
310         for ( ; pName != pNameEnd; ++pName, ++pValue, ++pPropertyValue )
311         {
312             pPropertyValue->Name = *pName;
313             pPropertyValue->Value = *pValue;
314         }
315 
316         return aPropertyValues;
317     }
318 
319     //--------------------------------------------------------------------
setPropertyValues(const Sequence<PropertyValue> & _rProps)320     void PropertyBagHelper::setPropertyValues( const Sequence< PropertyValue >& _rProps )
321     {
322         // RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "forms", "dev@dba.openoffice.org", "PropertyBagHelper::setPropertyValues" );
323         ::osl::ClearableMutexGuard aGuard( m_rContext.getMutex() );
324         impl_nts_checkDisposed_throw();
325 
326         sal_Int32 nPropertyValues = _rProps.getLength();
327 
328         // XMultiPropertySet::setPropertyValues expects its arguments to be sorted by name
329         // while XPropertyAccess::setPropertyValues doesn't. So first of all, sort.
330         Sequence< PropertyValue > aSortedProps( _rProps );
331         ::std::sort( aSortedProps.getArray(), aSortedProps.getArray() + nPropertyValues, PropertyValueLessByName() );
332 
333         // also, XPropertyAccess::setPropertyValues is expected to throw an UnknownPropertyException
334         // for unsupported properties, while XMultiPropertySet::setPropertyValues is expected to ignore
335         // those. So, check for unsupported properties first.
336         ::comphelper::OPropertyArrayAggregationHelper& rArrayHelper( impl_ts_getArrayHelper() );
337         for (   const PropertyValue* pProperties = aSortedProps.getConstArray();
338                 pProperties != aSortedProps.getConstArray() + nPropertyValues;
339                 ++pProperties
340             )
341         {
342             if ( !rArrayHelper.hasPropertyByName( pProperties->Name ) )
343                 throw UnknownPropertyException( pProperties->Name, m_rContext.getPropertiesInterface() );
344         }
345 
346         // Now finally split into a Name and a Value sequence, and forward to
347         // XMultiPropertySet::setPropertyValues
348         Sequence< ::rtl::OUString > aNames( nPropertyValues );
349         ::std::transform( aSortedProps.getConstArray(), aSortedProps.getConstArray() + nPropertyValues,
350             aNames.getArray(), SelectNameOfPropertyValue() );
351 
352         Sequence< Any > aValues( nPropertyValues );
353         ::std::transform( aSortedProps.getConstArray(), aSortedProps.getConstArray() + nPropertyValues,
354             aValues.getArray(), SelectValueOfPropertyValue() );
355 
356         Reference< XMultiPropertySet > xMe( m_rContext.getPropertiesInterface(), UNO_QUERY_THROW );
357 
358         aGuard.clear();
359         xMe->setPropertyValues( aNames, aValues );
360     }
361 
362 //........................................................................
363 } // namespace frm
364 //........................................................................
365 
366