Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <comphelper/propertycontainerhelper.hxx>
21 : #include <comphelper/property.hxx>
22 : #include <osl/diagnose.h>
23 : #include <uno/data.h>
24 : #include <com/sun/star/uno/genfunc.h>
25 : #include <com/sun/star/beans/PropertyAttribute.hpp>
26 : #include <com/sun/star/beans/UnknownPropertyException.hpp>
27 : #include <rtl/ustrbuf.hxx>
28 :
29 : #include <algorithm>
30 :
31 :
32 : namespace comphelper
33 : {
34 :
35 :
36 : using namespace ::com::sun::star::uno;
37 : using namespace ::com::sun::star::lang;
38 : using namespace ::com::sun::star::beans;
39 :
40 :
41 : namespace
42 : {
43 : // comparing two property descriptions
44 : struct PropertyDescriptionHandleCompare : public ::std::binary_function< PropertyDescription, PropertyDescription, bool >
45 : {
46 2896174 : bool operator() (const PropertyDescription& x, const PropertyDescription& y) const
47 : {
48 2896174 : return x.aProperty.Handle < y.aProperty.Handle;
49 : }
50 : };
51 : // comparing two property descriptions (by name)
52 58299 : struct PropertyDescriptionNameMatch : public ::std::unary_function< PropertyDescription, bool >
53 : {
54 : OUString m_rCompare;
55 19433 : explicit PropertyDescriptionNameMatch( const OUString& _rCompare ) : m_rCompare( _rCompare ) { }
56 :
57 190470 : bool operator() (const PropertyDescription& x ) const
58 : {
59 190470 : return x.aProperty.Name.equals(m_rCompare);
60 : }
61 : };
62 : }
63 :
64 126167 : OPropertyContainerHelper::OPropertyContainerHelper()
65 : {
66 126167 : }
67 :
68 :
69 125896 : OPropertyContainerHelper::~OPropertyContainerHelper()
70 : {
71 125896 : }
72 :
73 :
74 245486 : void OPropertyContainerHelper::registerProperty(const OUString& _rName, sal_Int32 _nHandle,
75 : sal_Int32 _nAttributes, void* _pPointerToMember, const Type& _rMemberType)
76 : {
77 : OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) == 0,
78 : "OPropertyContainerHelper::registerProperty: don't use this for properties which may be void ! There is a method called \"registerMayBeVoidProperty\" for this !");
79 : OSL_ENSURE(!_rMemberType.equals(cppu::UnoType<Any>::get()),
80 : "OPropertyContainerHelper::registerProperty: don't give my the type of an uno::Any ! Really can't handle this !");
81 : OSL_ENSURE(_pPointerToMember,
82 : "OPropertyContainerHelper::registerProperty: you gave me nonsense : the pointer must be non-NULL");
83 :
84 245486 : PropertyDescription aNewProp;
85 245486 : aNewProp.aProperty = Property( _rName, _nHandle, _rMemberType, (sal_Int16)_nAttributes );
86 245486 : aNewProp.eLocated = PropertyDescription::ltDerivedClassRealType;
87 245486 : aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
88 :
89 245486 : implPushBackProperty(aNewProp);
90 245486 : }
91 :
92 :
93 21 : void OPropertyContainerHelper::revokeProperty( sal_Int32 _nHandle )
94 : {
95 21 : PropertiesIterator aPos = searchHandle( _nHandle );
96 21 : if ( aPos == m_aProperties.end() )
97 0 : throw UnknownPropertyException();
98 21 : m_aProperties.erase( aPos );
99 21 : }
100 :
101 :
102 14390 : void OPropertyContainerHelper::registerMayBeVoidProperty(const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes,
103 : Any* _pPointerToMember, const Type& _rExpectedType)
104 : {
105 : OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) != 0,
106 : "OPropertyContainerHelper::registerMayBeVoidProperty: why calling this when the attributes say nothing about may-be-void ?");
107 : OSL_ENSURE(!_rExpectedType.equals(cppu::UnoType<Any>::get()),
108 : "OPropertyContainerHelper::registerMayBeVoidProperty: don't give my the type of an uno::Any ! Really can't handle this !");
109 : OSL_ENSURE(_pPointerToMember,
110 : "OPropertyContainerHelper::registerMayBeVoidProperty: you gave me nonsense : the pointer must be non-NULL");
111 :
112 14390 : _nAttributes |= PropertyAttribute::MAYBEVOID;
113 :
114 14390 : PropertyDescription aNewProp;
115 14390 : aNewProp.aProperty = Property( _rName, _nHandle, _rExpectedType, (sal_Int16)_nAttributes );
116 14390 : aNewProp.eLocated = PropertyDescription::ltDerivedClassAnyType;
117 14390 : aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
118 :
119 14390 : implPushBackProperty(aNewProp);
120 14390 : }
121 :
122 :
123 :
124 23327 : void OPropertyContainerHelper::registerPropertyNoMember(const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes,
125 : const Type& _rType, const void* _pInitialValue)
126 : {
127 : OSL_ENSURE(!_rType.equals(cppu::UnoType<Any>::get()),
128 : "OPropertyContainerHelper::registerPropertyNoMember : don't give my the type of an uno::Any ! Really can't handle this !");
129 : OSL_ENSURE(_pInitialValue || ((_nAttributes & PropertyAttribute::MAYBEVOID) != 0),
130 : "OPropertyContainerHelper::registerPropertyNoMember : you should not omit the initial value if the property can't be void ! This will definitivly crash later !");
131 :
132 23327 : PropertyDescription aNewProp;
133 23327 : aNewProp.aProperty = Property( _rName, _nHandle, _rType, (sal_Int16)_nAttributes );
134 23327 : aNewProp.eLocated = PropertyDescription::ltHoldMyself;
135 23327 : aNewProp.aLocation.nOwnClassVectorIndex = m_aHoldProperties.size();
136 23327 : if (_pInitialValue)
137 22994 : m_aHoldProperties.push_back(Any(_pInitialValue, _rType));
138 : else
139 333 : m_aHoldProperties.push_back(Any());
140 :
141 23327 : implPushBackProperty(aNewProp);
142 23327 : }
143 :
144 :
145 387657 : bool OPropertyContainerHelper::isRegisteredProperty( sal_Int32 _nHandle ) const
146 : {
147 387657 : return const_cast< OPropertyContainerHelper* >( this )->searchHandle( _nHandle ) != m_aProperties.end();
148 : }
149 :
150 :
151 19410 : bool OPropertyContainerHelper::isRegisteredProperty( const OUString& _rName ) const
152 : {
153 : // TODO: the current structure is from a time where properties were
154 : // static, not dynamic. Since we allow that properties are also dynamic,
155 : // i.e. registered and revoked even though the XPropertySet has already been
156 : // accessed, a vector is not really the best data structure anymore ...
157 :
158 : ConstPropertiesIterator pos = ::std::find_if(
159 : m_aProperties.begin(),
160 : m_aProperties.end(),
161 : PropertyDescriptionNameMatch( _rName )
162 19410 : );
163 19410 : return pos != m_aProperties.end();
164 : }
165 :
166 :
167 : namespace
168 : {
169 : struct ComparePropertyHandles
170 : {
171 399853 : bool operator()( const PropertyDescription& _rLHS, const PropertyDescription& _nRHS ) const
172 : {
173 399853 : return _rLHS.aProperty.Handle < _nRHS.aProperty.Handle;
174 : }
175 : };
176 : }
177 :
178 :
179 283203 : void OPropertyContainerHelper::implPushBackProperty(const PropertyDescription& _rProp)
180 : {
181 : #ifdef DBG_UTIL
182 : for ( PropertiesIterator checkConflicts = m_aProperties.begin();
183 : checkConflicts != m_aProperties.end();
184 : ++checkConflicts
185 : )
186 : {
187 : OSL_ENSURE(checkConflicts->aProperty.Name != _rProp.aProperty.Name, "OPropertyContainerHelper::implPushBackProperty: name already exists!");
188 : OSL_ENSURE(checkConflicts->aProperty.Handle != _rProp.aProperty.Handle, "OPropertyContainerHelper::implPushBackProperty: handle already exists!");
189 : }
190 : #endif
191 :
192 : PropertiesIterator pos = ::std::lower_bound(
193 : m_aProperties.begin(), m_aProperties.end(),
194 283203 : _rProp, ComparePropertyHandles() );
195 :
196 283203 : m_aProperties.insert( pos, _rProp );
197 283203 : }
198 :
199 :
200 : namespace
201 : {
202 5 : void lcl_throwIllegalPropertyValueTypeException( const PropertyDescription& _rProperty, const Any& _rValue )
203 : {
204 5 : OUStringBuffer aErrorMessage;
205 5 : aErrorMessage.appendAscii( "The given value cannot be converted to the required property type." );
206 5 : aErrorMessage.appendAscii( "\n(property name \"" );
207 5 : aErrorMessage.append( _rProperty.aProperty.Name );
208 5 : aErrorMessage.appendAscii( "\", found value type \"" );
209 5 : aErrorMessage.append( _rValue.getValueType().getTypeName() );
210 5 : aErrorMessage.appendAscii( "\", required property type \"" );
211 5 : aErrorMessage.append( _rProperty.aProperty.Type.getTypeName() );
212 5 : aErrorMessage.appendAscii( "\")" );
213 5 : throw IllegalArgumentException( aErrorMessage.makeStringAndClear(), NULL, 4 );
214 : }
215 : }
216 :
217 :
218 32044 : bool OPropertyContainerHelper::convertFastPropertyValue(
219 : Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
220 : {
221 32044 : bool bModified = false;
222 :
223 : // get the property somebody is asking for
224 32044 : PropertiesIterator aPos = searchHandle(_nHandle);
225 32044 : if (aPos == m_aProperties.end())
226 : {
227 : OSL_FAIL( "OPropertyContainerHelper::convertFastPropertyValue: unknown handle!" );
228 : // should not happen if the derived class has built a correct property set info helper to be used by
229 : // our base class OPropertySetHelper
230 0 : return bModified;
231 : }
232 :
233 32044 : switch (aPos->eLocated)
234 : {
235 : // similar handling for the two cases where the value is stored in an any
236 : case PropertyDescription::ltHoldMyself:
237 : case PropertyDescription::ltDerivedClassAnyType:
238 : {
239 8363 : bool bMayBeVoid = ((aPos->aProperty.Attributes & PropertyAttribute::MAYBEVOID) != 0);
240 :
241 :
242 : // non modifiable version of the value-to-be-set
243 8363 : Any aNewRequestedValue( _rValue );
244 :
245 : // normalization
246 : // #i29490#
247 8363 : if ( !aNewRequestedValue.getValueType().equals( aPos->aProperty.Type ) )
248 : { // the actually given value is not of the same type as the one required
249 6093 : Any aProperlyTyped( NULL, aPos->aProperty.Type.getTypeLibType() );
250 :
251 6093 : if ( uno_type_assignData(
252 12186 : const_cast< void* >( aProperlyTyped.getValue() ), aProperlyTyped.getValueType().getTypeLibType(),
253 12186 : const_cast< void* >( aNewRequestedValue.getValue() ), aNewRequestedValue.getValueType().getTypeLibType(),
254 : reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
255 : reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
256 : reinterpret_cast< uno_ReleaseFunc >( cpp_release )
257 18279 : )
258 : )
259 : {
260 : // we were able to query the given XInterface-derivee for the interface
261 : // which is required for this property
262 223 : aNewRequestedValue = aProperlyTyped;
263 6093 : }
264 : }
265 :
266 : // argument check
267 24286 : if ( ! ( (bMayBeVoid && !aNewRequestedValue.hasValue()) // void is allowed if the attribute says so
268 2494 : || (aNewRequestedValue.getValueType().equals(aPos->aProperty.Type)) // else the types have to be equal
269 10857 : )
270 : )
271 : {
272 1 : lcl_throwIllegalPropertyValueTypeException( *aPos, _rValue );
273 : }
274 :
275 8362 : Any* pPropContainer = NULL;
276 : // the pointer to the any which holds the property value, no matter if located in the derived class
277 : // or in out vector
278 :
279 8362 : if (PropertyDescription::ltHoldMyself == aPos->eLocated)
280 : {
281 : OSL_ENSURE(aPos->aLocation.nOwnClassVectorIndex < (sal_Int32)m_aHoldProperties.size(),
282 : "OPropertyContainerHelper::convertFastPropertyValue: invalid position !");
283 856 : PropertyContainerIterator aIter = m_aHoldProperties.begin() + aPos->aLocation.nOwnClassVectorIndex;
284 856 : pPropContainer = &(*aIter);
285 : }
286 : else
287 7506 : pPropContainer = static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
288 :
289 : // check if the new value differs from the current one
290 8362 : if (!pPropContainer->hasValue() || !aNewRequestedValue.hasValue())
291 6876 : bModified = pPropContainer->hasValue() != aNewRequestedValue.hasValue();
292 : else
293 : bModified = !uno_type_equalData(
294 2972 : const_cast< void* >( pPropContainer->getValue() ), aPos->aProperty.Type.getTypeLibType(),
295 2972 : const_cast< void* >( aNewRequestedValue.getValue() ), aPos->aProperty.Type.getTypeLibType(),
296 : reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
297 : reinterpret_cast< uno_ReleaseFunc >( cpp_release )
298 4458 : );
299 :
300 8362 : if (bModified)
301 : {
302 1216 : _rOldValue = *pPropContainer;
303 1216 : _rConvertedValue = aNewRequestedValue;
304 8363 : }
305 : }
306 8362 : break;
307 : case PropertyDescription::ltDerivedClassRealType:
308 : // let the UNO runtime library do any possible conversion
309 : // this may include a change of the type - for instance, if a LONG is required,
310 : // but a short is given, then this is valid, as it can be converted without any potential
311 : // data loss
312 :
313 23681 : Any aProperlyTyped;
314 23681 : const Any* pNewValue = &_rValue;
315 :
316 23681 : if (!_rValue.getValueType().equals(aPos->aProperty.Type))
317 : {
318 6 : bool bConverted = false;
319 :
320 : // a temporary any of the correct (required) type
321 6 : aProperlyTyped = Any( NULL, aPos->aProperty.Type.getTypeLibType() );
322 : // (need this as we do not want to overwrite the derived class member here)
323 :
324 6 : if ( uno_type_assignData(
325 12 : const_cast<void*>(aProperlyTyped.getValue()), aProperlyTyped.getValueType().getTypeLibType(),
326 12 : const_cast<void*>(_rValue.getValue()), _rValue.getValueType().getTypeLibType(),
327 : reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
328 : reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
329 : reinterpret_cast< uno_ReleaseFunc >( cpp_release )
330 18 : )
331 : )
332 : {
333 : // could query for the requested interface
334 2 : bConverted = true;
335 2 : pNewValue = &aProperlyTyped;
336 : }
337 :
338 6 : if ( !bConverted )
339 4 : lcl_throwIllegalPropertyValueTypeException( *aPos, _rValue );
340 : }
341 :
342 : // from here on, we should have the proper type
343 : OSL_ENSURE( pNewValue->getValueType() == aPos->aProperty.Type,
344 : "OPropertyContainerHelper::convertFastPropertyValue: conversion failed!" );
345 : bModified = !uno_type_equalData(
346 47354 : aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type.getTypeLibType(),
347 47354 : const_cast<void*>(pNewValue->getValue()), aPos->aProperty.Type.getTypeLibType(),
348 : reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
349 : reinterpret_cast< uno_ReleaseFunc >( cpp_release )
350 71031 : );
351 :
352 23677 : if (bModified)
353 : {
354 9949 : _rOldValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
355 9949 : _rConvertedValue = *pNewValue;
356 : }
357 23681 : break;
358 : }
359 :
360 32039 : return bModified;
361 : }
362 :
363 :
364 27314 : bool OPropertyContainerHelper::setFastPropertyValue(sal_Int32 _nHandle, const Any& _rValue)
365 : {
366 : // get the property somebody is asking for
367 27314 : PropertiesIterator aPos = searchHandle(_nHandle);
368 27314 : if (aPos == m_aProperties.end())
369 : {
370 : OSL_FAIL( "OPropertyContainerHelper::setFastPropertyValue: unknown handle!" );
371 : // should not happen if the derived class has built a correct property set info helper to be used by
372 : // our base class OPropertySetHelper
373 0 : return false;
374 : }
375 :
376 27314 : bool bSuccess = true;
377 :
378 27314 : switch (aPos->eLocated)
379 : {
380 : case PropertyDescription::ltHoldMyself:
381 21 : m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex] = _rValue;
382 21 : break;
383 :
384 : case PropertyDescription::ltDerivedClassAnyType:
385 2929 : *static_cast< Any* >(aPos->aLocation.pDerivedClassMember) = _rValue;
386 2929 : break;
387 :
388 : case PropertyDescription::ltDerivedClassRealType:
389 : // copy the data from the to-be-set value
390 : bSuccess = uno_type_assignData(
391 48728 : aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type.getTypeLibType(),
392 48728 : const_cast< void* >( _rValue.getValue() ), _rValue.getValueType().getTypeLibType(),
393 : reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
394 : reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
395 73092 : reinterpret_cast< uno_ReleaseFunc >( cpp_release ) );
396 :
397 : OSL_ENSURE( bSuccess,
398 : "OPropertyContainerHelper::setFastPropertyValue: ooops .... the value could not be assigned!");
399 :
400 24364 : break;
401 : }
402 :
403 27314 : return bSuccess;
404 : }
405 :
406 397510 : void OPropertyContainerHelper::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
407 : {
408 : // get the property somebody is asking for
409 397510 : PropertiesIterator aPos = const_cast<OPropertyContainerHelper*>(this)->searchHandle(_nHandle);
410 397510 : if (aPos == m_aProperties.end())
411 : {
412 : OSL_FAIL( "OPropertyContainerHelper::getFastPropertyValue: unknown handle!" );
413 : // should not happen if the derived class has built a correct property set info helper to be used by
414 : // our base class OPropertySetHelper
415 397510 : return;
416 : }
417 :
418 397510 : switch (aPos->eLocated)
419 : {
420 : case PropertyDescription::ltHoldMyself:
421 : OSL_ENSURE(aPos->aLocation.nOwnClassVectorIndex < (sal_Int32)m_aHoldProperties.size(),
422 : "OPropertyContainerHelper::convertFastPropertyValue: invalid position !");
423 51321 : _rValue = m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex];
424 51321 : break;
425 : case PropertyDescription::ltDerivedClassAnyType:
426 15636 : _rValue = *static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
427 15636 : break;
428 : case PropertyDescription::ltDerivedClassRealType:
429 330553 : _rValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
430 330553 : break;
431 : }
432 : }
433 :
434 :
435 844546 : OPropertyContainerHelper::PropertiesIterator OPropertyContainerHelper::searchHandle(sal_Int32 _nHandle)
436 : {
437 844546 : PropertyDescription aHandlePropDesc;
438 844546 : aHandlePropDesc.aProperty.Handle = _nHandle;
439 : // search a lower bound
440 : PropertiesIterator aLowerBound = ::std::lower_bound(
441 : m_aProperties.begin(),
442 : m_aProperties.end(),
443 : aHandlePropDesc,
444 844546 : PropertyDescriptionHandleCompare());
445 :
446 : // check for identity
447 844546 : if ((aLowerBound != m_aProperties.end()) && aLowerBound->aProperty.Handle != _nHandle)
448 39593 : aLowerBound = m_aProperties.end();
449 :
450 844546 : return aLowerBound;
451 : }
452 :
453 :
454 23 : const Property& OPropertyContainerHelper::getProperty( const OUString& _rName ) const
455 : {
456 : ConstPropertiesIterator pos = ::std::find_if(
457 : m_aProperties.begin(),
458 : m_aProperties.end(),
459 : PropertyDescriptionNameMatch( _rName )
460 23 : );
461 23 : if ( pos == m_aProperties.end() )
462 1 : throw UnknownPropertyException( _rName );
463 :
464 22 : return pos->aProperty;
465 : }
466 :
467 :
468 6982 : void OPropertyContainerHelper::describeProperties(Sequence< Property >& _rProps) const
469 : {
470 6982 : Sequence< Property > aOwnProps(m_aProperties.size());
471 6982 : Property* pOwnProps = aOwnProps.getArray();
472 :
473 188169 : for ( ConstPropertiesIterator aLoop = m_aProperties.begin();
474 125446 : aLoop != m_aProperties.end();
475 : ++aLoop, ++pOwnProps
476 : )
477 : {
478 55741 : pOwnProps->Name = aLoop->aProperty.Name;
479 55741 : pOwnProps->Handle = aLoop->aProperty.Handle;
480 55741 : pOwnProps->Attributes = (sal_Int16)aLoop->aProperty.Attributes;
481 55741 : pOwnProps->Type = aLoop->aProperty.Type;
482 : }
483 :
484 : // as our property vector is sorted by handles, not by name, we have to sort aOwnProps
485 6982 : ::std::sort(aOwnProps.getArray(), aOwnProps.getArray() + aOwnProps.getLength(), PropertyCompareByName());
486 :
487 : // unfortunately the STL merge function does not allow the output range to overlap one of the input ranges,
488 : // so we need an extra sequence
489 13964 : Sequence< Property > aOutput;
490 6982 : aOutput.realloc(_rProps.getLength() + aOwnProps.getLength());
491 : // do the merge
492 6982 : ::std::merge( _rProps.getConstArray(), _rProps.getConstArray() + _rProps.getLength(), // input 1
493 6982 : aOwnProps.getConstArray(), aOwnProps.getConstArray() + aOwnProps.getLength(), // input 2
494 : aOutput.getArray(), // output
495 : PropertyCompareByName() // compare operator
496 20946 : );
497 :
498 : // copy the output
499 13964 : _rProps = aOutput;
500 6982 : }
501 :
502 :
503 : } // namespace comphelper
504 :
505 :
506 :
507 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|