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_extensions.hxx"
26 #include "propertycomposer.hxx"
27 
28 /** === begin UNO includes === **/
29 #include <com/sun/star/lang/NullPointerException.hpp>
30 #include <com/sun/star/lang/IllegalArgumentException.hpp>
31 /** === end UNO includes === **/
32 #include <osl/diagnose.h>
33 #include <tools/diagnose_ex.h>
34 
35 #include <functional>
36 #include <algorithm>
37 #include <map>
38 
39 //........................................................................
40 namespace pcr
41 {
42 //........................................................................
43 
44     using namespace ::com::sun::star::uno;
45     using namespace ::com::sun::star::beans;
46     using namespace ::com::sun::star::lang;
47     using namespace ::com::sun::star::inspection;
48 
49 	//====================================================================
50 	//= helper
51 	//====================================================================
52     namespace
53     {
54         //----------------------------------------------------------------
55         struct SetPropertyValue : public ::std::unary_function< Reference< XPropertyHandler >, void >
56         {
57             ::rtl::OUString sPropertyName;
58             const Any&      rValue;
SetPropertyValuepcr::__anon95d7f75a0111::SetPropertyValue59             SetPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rValue ) : sPropertyName( _rPropertyName ), rValue( _rValue ) { }
operator ()pcr::__anon95d7f75a0111::SetPropertyValue60             void operator()( const Reference< XPropertyHandler >& _rHandler )
61             {
62                 _rHandler->setPropertyValue( sPropertyName, rValue );
63             }
64         };
65 
66         //----------------------------------------------------------------
67         template < class BagType >
putIntoBag(const Sequence<typename BagType::value_type> & _rArray,BagType & _rBag)68         void putIntoBag( const Sequence< typename BagType::value_type >& _rArray, BagType& /* [out] */ _rBag )
69         {
70             ::std::copy( _rArray.getConstArray(), _rArray.getConstArray() + _rArray.getLength(),
71                 ::std::insert_iterator< BagType >( _rBag, _rBag.begin() ) );
72         }
73 
74         //----------------------------------------------------------------
75         template < class BagType >
copyBagToArray(const BagType & _rBag,Sequence<typename BagType::value_type> & _rArray)76         void copyBagToArray( const BagType& /* [out] */ _rBag, Sequence< typename BagType::value_type >& _rArray )
77         {
78             _rArray.realloc( _rBag.size() );
79             ::std::copy( _rBag.begin(), _rBag.end(), _rArray.getArray() );
80         }
81     }
82 
83     //====================================================================
84     //= PropertyComposer
85 	//====================================================================
86 
87     // TODO: there are various places where we determine the first handler in our array which
88     // supports a given property id. This is, at the moment, done with searching all handlers,
89     // which is O( n * k ) at worst (n being the number of handlers, k being the maximum number
90     // of supported properties per handler). Shouldn't we cache this? So that it is O( log k )?
91 
92 	//--------------------------------------------------------------------
PropertyComposer(const::std::vector<Reference<XPropertyHandler>> & _rSlaveHandlers)93     PropertyComposer::PropertyComposer( const ::std::vector< Reference< XPropertyHandler > >& _rSlaveHandlers )
94         :PropertyComposer_Base          ( m_aMutex          )
95         ,m_aSlaveHandlers               ( _rSlaveHandlers   )
96         ,m_aPropertyListeners           ( m_aMutex          )
97         ,m_bSupportedPropertiesAreKnown ( false             )
98     {
99         if ( m_aSlaveHandlers.empty() )
100             throw IllegalArgumentException();
101 
102         osl_incrementInterlockedCount( &m_refCount );
103         {
104             Reference< XPropertyChangeListener > xMeMyselfAndI( this );
105             for (   HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
106                     loop != m_aSlaveHandlers.end();
107                     ++loop
108                 )
109             {
110                 if ( !loop->is() )
111                     throw NullPointerException();
112                 (*loop)->addPropertyChangeListener( xMeMyselfAndI );
113             }
114         }
115         osl_decrementInterlockedCount( &m_refCount );
116     }
117 
118     //--------------------------------------------------------------------
inspect(const Reference<XInterface> & _rxIntrospectee)119     void SAL_CALL PropertyComposer::inspect( const Reference< XInterface >& _rxIntrospectee ) throw (RuntimeException, NullPointerException)
120     {
121         MethodGuard aGuard( *this );
122 
123         for ( HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
124               loop != m_aSlaveHandlers.end();
125               ++loop
126             )
127         {
128             (*loop)->inspect( _rxIntrospectee );
129         }
130     }
131 
132     //--------------------------------------------------------------------
getPropertyValue(const::rtl::OUString & _rPropertyName)133     Any SAL_CALL PropertyComposer::getPropertyValue( const ::rtl::OUString& _rPropertyName ) throw (UnknownPropertyException, RuntimeException)
134     {
135         MethodGuard aGuard( *this );
136         return m_aSlaveHandlers[0]->getPropertyValue( _rPropertyName );
137     }
138 
139     //--------------------------------------------------------------------
setPropertyValue(const::rtl::OUString & _rPropertyName,const Any & _rValue)140     void SAL_CALL PropertyComposer::setPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rValue ) throw (UnknownPropertyException, RuntimeException)
141     {
142         MethodGuard aGuard( *this );
143         ::std::for_each( m_aSlaveHandlers.begin(), m_aSlaveHandlers.end(), SetPropertyValue( _rPropertyName, _rValue ) );
144     }
145 
146     //--------------------------------------------------------------------
convertToPropertyValue(const::rtl::OUString & _rPropertyName,const Any & _rControlValue)147     Any SAL_CALL PropertyComposer::convertToPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rControlValue ) throw (UnknownPropertyException, RuntimeException)
148     {
149         MethodGuard aGuard( *this );
150         return m_aSlaveHandlers[0]->convertToPropertyValue( _rPropertyName, _rControlValue );
151     }
152 
153     //--------------------------------------------------------------------
convertToControlValue(const::rtl::OUString & _rPropertyName,const Any & _rPropertyValue,const Type & _rControlValueType)154     Any SAL_CALL PropertyComposer::convertToControlValue( const ::rtl::OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType ) throw (UnknownPropertyException, RuntimeException)
155     {
156         MethodGuard aGuard( *this );
157         return m_aSlaveHandlers[0]->convertToControlValue( _rPropertyName, _rPropertyValue, _rControlValueType );
158     }
159 
160     //--------------------------------------------------------------------
getPropertyState(const::rtl::OUString & _rPropertyName)161     PropertyState SAL_CALL PropertyComposer::getPropertyState( const ::rtl::OUString& _rPropertyName ) throw (UnknownPropertyException, RuntimeException)
162     {
163         MethodGuard aGuard( *this );
164 
165         // assume DIRECT for the moment. This will stay this way if *all* slaves
166         // tell the property has DIRECT state, and if *all* values equal
167         PropertyState eState = PropertyState_DIRECT_VALUE;
168 
169         // check the master state
170         Reference< XPropertyHandler > xPrimary( *m_aSlaveHandlers.begin() );
171         Any aPrimaryValue = xPrimary->getPropertyValue( _rPropertyName );
172         eState = xPrimary->getPropertyState( _rPropertyName );
173 
174         // loop through the secondary sets
175         PropertyState eSecondaryState = PropertyState_DIRECT_VALUE;
176         for ( HandlerArray::const_iterator loop = ( m_aSlaveHandlers.begin() + 1 );
177               loop != m_aSlaveHandlers.end();
178               ++loop
179             )
180         {
181             // the secondary state
182             eSecondaryState = (*loop)->getPropertyState( _rPropertyName );
183 
184             // the secondary value
185             Any aSecondaryValue( (*loop)->getPropertyValue( _rPropertyName ) );
186 
187             if  (   ( PropertyState_AMBIGUOUS_VALUE == eSecondaryState )    // secondary is ambiguous
188                 ||  ( aPrimaryValue != aSecondaryValue )                    // unequal values
189                 )
190             {
191                 eState = PropertyState_AMBIGUOUS_VALUE;
192                 break;
193             }
194         }
195 
196         return eState;
197     }
198 
199     //--------------------------------------------------------------------
addPropertyChangeListener(const Reference<XPropertyChangeListener> & _rxListener)200     void SAL_CALL PropertyComposer::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) throw (RuntimeException)
201     {
202         MethodGuard aGuard( *this );
203         m_aPropertyListeners.addListener( _rxListener );
204     }
205 
206     //--------------------------------------------------------------------
removePropertyChangeListener(const Reference<XPropertyChangeListener> & _rxListener)207     void SAL_CALL PropertyComposer::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) throw (RuntimeException)
208     {
209         MethodGuard aGuard( *this );
210         m_aPropertyListeners.removeListener( _rxListener );
211     }
212 
213     //--------------------------------------------------------------------
getSupportedProperties()214     Sequence< Property > SAL_CALL PropertyComposer::getSupportedProperties() throw (RuntimeException)
215     {
216         MethodGuard aGuard( *this );
217 
218         if ( !m_bSupportedPropertiesAreKnown )
219         {
220             // we support a property if and only if all of our slaves support it
221 
222             // initially, use all the properties of an arbitrary handler (we take the first one)
223             putIntoBag( (*m_aSlaveHandlers.begin())->getSupportedProperties(), m_aSupportedProperties );
224 
225             // now intersect with the properties of *all* other handlers
226             for ( HandlerArray::const_iterator loop = ( m_aSlaveHandlers.begin() + 1 );
227                 loop != m_aSlaveHandlers.end();
228                 ++loop
229                 )
230             {
231                 // the properties supported by the current handler
232                 PropertyBag aThisRound;
233                 putIntoBag( (*loop)->getSupportedProperties(), aThisRound );
234 
235                 // the intersection of those properties with all we already have
236                 PropertyBag aIntersection;
237                 ::std::set_intersection( aThisRound.begin(), aThisRound.end(), m_aSupportedProperties.begin(), m_aSupportedProperties.end(),
238                     ::std::insert_iterator< PropertyBag >( aIntersection, aIntersection.begin() ), PropertyLessByName() );
239 
240                 m_aSupportedProperties.swap( aIntersection );
241                 if ( m_aSupportedProperties.empty() )
242                     break;
243             }
244 
245             // remove those properties which are not composable
246             for (   PropertyBag::iterator check = m_aSupportedProperties.begin();
247                     check != m_aSupportedProperties.end();
248                 )
249             {
250                 sal_Bool bIsComposable = isComposable( check->Name );
251                 if ( !bIsComposable )
252                 {
253                     PropertyBag::iterator next = check; ++next;
254                     m_aSupportedProperties.erase( check );
255                     check = next;
256                 }
257                 else
258                     ++check;
259             }
260 
261             m_bSupportedPropertiesAreKnown = true;
262         }
263 
264         Sequence< Property > aSurvived;
265         copyBagToArray( m_aSupportedProperties, aSurvived );
266         return aSurvived;
267     }
268 
269     //--------------------------------------------------------------------
uniteStringArrays(const PropertyComposer::HandlerArray & _rHandlers,Sequence<::rtl::OUString> (SAL_CALL XPropertyHandler::* pGetter)(void),Sequence<::rtl::OUString> & _rUnion)270     void uniteStringArrays( const PropertyComposer::HandlerArray& _rHandlers, Sequence< ::rtl::OUString > (SAL_CALL XPropertyHandler::*pGetter)( void ),
271         Sequence< ::rtl::OUString >& /* [out] */ _rUnion )
272     {
273         ::std::set< ::rtl::OUString > aUnitedBag;
274 
275         Sequence< ::rtl::OUString > aThisRound;
276         for ( PropertyComposer::HandlerArray::const_iterator loop = _rHandlers.begin();
277               loop != _rHandlers.end();
278               ++loop
279             )
280         {
281             aThisRound = (loop->get()->*pGetter)();
282             putIntoBag( aThisRound, aUnitedBag );
283         }
284 
285         copyBagToArray( aUnitedBag, _rUnion );
286     }
287 
288     //--------------------------------------------------------------------
getSupersededProperties()289     Sequence< ::rtl::OUString > SAL_CALL PropertyComposer::getSupersededProperties( ) throw (RuntimeException)
290     {
291         MethodGuard aGuard( *this );
292 
293         // we supersede those properties which are superseded by at least one of our slaves
294         Sequence< ::rtl::OUString > aSuperseded;
295         uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getSupersededProperties, aSuperseded );
296         return aSuperseded;
297     }
298 
299     //--------------------------------------------------------------------
getActuatingProperties()300     Sequence< ::rtl::OUString > SAL_CALL PropertyComposer::getActuatingProperties( ) throw (RuntimeException)
301     {
302         MethodGuard aGuard( *this );
303 
304         // we're interested in those properties which at least one handler wants to have
305         Sequence< ::rtl::OUString > aActuating;
306         uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getActuatingProperties, aActuating );
307         return aActuating;
308     }
309 
310     //--------------------------------------------------------------------
describePropertyLine(const::rtl::OUString & _rPropertyName,const Reference<XPropertyControlFactory> & _rxControlFactory)311     LineDescriptor SAL_CALL PropertyComposer::describePropertyLine( const ::rtl::OUString& _rPropertyName,
312         const Reference< XPropertyControlFactory >& _rxControlFactory )
313         throw (UnknownPropertyException, NullPointerException, RuntimeException)
314     {
315         MethodGuard aGuard( *this );
316         return m_aSlaveHandlers[0]->describePropertyLine( _rPropertyName, _rxControlFactory );
317     }
318 
319     //--------------------------------------------------------------------
isComposable(const::rtl::OUString & _rPropertyName)320     ::sal_Bool SAL_CALL PropertyComposer::isComposable( const ::rtl::OUString& _rPropertyName ) throw (UnknownPropertyException, RuntimeException)
321     {
322         MethodGuard aGuard( *this );
323         return m_aSlaveHandlers[0]->isComposable( _rPropertyName );
324     }
325 
326     //--------------------------------------------------------------------
onInteractivePropertySelection(const::rtl::OUString & _rPropertyName,sal_Bool _bPrimary,Any & _rData,const Reference<XObjectInspectorUI> & _rxInspectorUI)327     InteractiveSelectionResult SAL_CALL PropertyComposer::onInteractivePropertySelection( const ::rtl::OUString& _rPropertyName, sal_Bool _bPrimary, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI ) throw (UnknownPropertyException, NullPointerException, RuntimeException)
328     {
329         if ( !_rxInspectorUI.is() )
330             throw NullPointerException();
331 
332         MethodGuard aGuard( *this );
333 
334         impl_ensureUIRequestComposer( _rxInspectorUI );
335         ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
336 
337         // ask the first of the handlers
338         InteractiveSelectionResult eResult = (*m_aSlaveHandlers.begin())->onInteractivePropertySelection(
339                 _rPropertyName,
340                 _bPrimary,
341                 _rData,
342                 m_pUIRequestComposer->getUIForPropertyHandler( *m_aSlaveHandlers.begin() )
343             );
344 
345         switch ( eResult )
346         {
347         case InteractiveSelectionResult_Cancelled:
348             // fine
349             break;
350 
351         case InteractiveSelectionResult_Success:
352         case InteractiveSelectionResult_Pending:
353             OSL_ENSURE( false, "PropertyComposer::onInteractivePropertySelection: no chance to forward the new value to the other handlers!" );
354             // This means that we cannot know the new property value, which either has already been set
355             // at the first component ("Success"), or will be set later on once the asynchronous input
356             // is finished ("Pending"). So, we also cannot forward this new property value to the other
357             // handlers.
358             // We would need to be a listener at the property at the first component, but even this wouldn't
359             // be sufficient, since the property handler is free to change *any* property during a dedicated
360             // property UI.
361             eResult = InteractiveSelectionResult_Cancelled;
362             break;
363 
364         case InteractiveSelectionResult_ObtainedValue:
365             // OK. Our own caller will pass this as setPropertyValue, and we will then pass it to
366             // all slave handlers
367             break;
368 
369         default:
370             OSL_ENSURE( false, "OPropertyBrowserController::onInteractivePropertySelection: unknown result value!" );
371             break;
372         }
373 
374         return eResult;
375     }
376 
377     //--------------------------------------------------------------------
impl_ensureUIRequestComposer(const Reference<XObjectInspectorUI> & _rxInspectorUI)378     void PropertyComposer::impl_ensureUIRequestComposer( const Reference< XObjectInspectorUI >& _rxInspectorUI )
379     {
380         OSL_ENSURE( !m_pUIRequestComposer.get() || m_pUIRequestComposer->getDelegatorUI().get() == _rxInspectorUI.get(),
381             "PropertyComposer::impl_ensureUIRequestComposer: somebody's changing the horse in the mid of the race!" );
382 
383         if ( !m_pUIRequestComposer.get() )
384             m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( _rxInspectorUI, this ) );
385     }
386 
387     //--------------------------------------------------------------------
actuatingPropertyChanged(const::rtl::OUString & _rActuatingPropertyName,const Any & _rNewValue,const Any & _rOldValue,const Reference<XObjectInspectorUI> & _rxInspectorUI,sal_Bool _bFirstTimeInit)388     void SAL_CALL PropertyComposer::actuatingPropertyChanged( const ::rtl::OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& _rOldValue, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) throw (NullPointerException, RuntimeException)
389     {
390         if ( !_rxInspectorUI.is() )
391             throw NullPointerException();
392 
393         MethodGuard aGuard( *this );
394 
395         impl_ensureUIRequestComposer( _rxInspectorUI );
396         ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
397 
398         // ask all handlers which expressed interest in this particular property, and "compose" their
399         // commands for the UIUpdater
400         for (   HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
401                 loop != m_aSlaveHandlers.end();
402                 ++loop
403             )
404         {
405             // TODO: make this cheaper (cache it?)
406             const StlSyntaxSequence< ::rtl::OUString > aThisHandlersActuatingProps = (*loop)->getActuatingProperties();
407             for (   StlSyntaxSequence< ::rtl::OUString >::const_iterator loopProps = aThisHandlersActuatingProps.begin();
408                     loopProps != aThisHandlersActuatingProps.end();
409                     ++loopProps
410                 )
411             {
412                 if ( *loopProps == _rActuatingPropertyName )
413                 {
414                     (*loop)->actuatingPropertyChanged( _rActuatingPropertyName, _rNewValue, _rOldValue,
415                         m_pUIRequestComposer->getUIForPropertyHandler( *loop ),
416                         _bFirstTimeInit );
417                     break;
418                 }
419             }
420         }
421     }
422 
423     //--------------------------------------------------------------------
IMPLEMENT_FORWARD_XCOMPONENT(PropertyComposer,PropertyComposer_Base)424     IMPLEMENT_FORWARD_XCOMPONENT( PropertyComposer, PropertyComposer_Base )
425 
426     //--------------------------------------------------------------------
427     void SAL_CALL PropertyComposer::disposing()
428     {
429         MethodGuard aGuard( *this );
430 
431         // dispose our slave handlers
432         for ( PropertyComposer::HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
433               loop != m_aSlaveHandlers.end();
434               ++loop
435             )
436         {
437             (*loop)->removePropertyChangeListener( this );
438             (*loop)->dispose();
439         }
440 
441         clearContainer( m_aSlaveHandlers );
442 
443         if ( m_pUIRequestComposer.get() )
444             m_pUIRequestComposer->dispose();
445         m_pUIRequestComposer.reset();
446     }
447 
448     //--------------------------------------------------------------------
propertyChange(const PropertyChangeEvent & evt)449     void SAL_CALL PropertyComposer::propertyChange( const PropertyChangeEvent& evt ) throw (RuntimeException)
450     {
451         if ( !impl_isSupportedProperty_nothrow( evt.PropertyName ) )
452             // A slave handler might fire events for more properties than we support. Ignore those.
453             return;
454 
455         PropertyChangeEvent aTranslatedEvent( evt );
456         try
457         {
458             aTranslatedEvent.NewValue = getPropertyValue( evt.PropertyName );
459         }
460         catch( const Exception& )
461         {
462         	DBG_UNHANDLED_EXCEPTION();
463         }
464         m_aPropertyListeners.notify( aTranslatedEvent, &XPropertyChangeListener::propertyChange );
465     }
466 
467     //--------------------------------------------------------------------
disposing(const EventObject & Source)468     void SAL_CALL PropertyComposer::disposing( const EventObject& Source ) throw (RuntimeException)
469     {
470         MethodGuard aGuard( *this );
471         m_aPropertyListeners.disposing( Source );
472     }
473 
474     //--------------------------------------------------------------------
suspend(sal_Bool _bSuspend)475     sal_Bool SAL_CALL PropertyComposer::suspend( sal_Bool _bSuspend ) throw (RuntimeException)
476     {
477         MethodGuard aGuard( *this );
478         for ( PropertyComposer::HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
479               loop != m_aSlaveHandlers.end();
480               ++loop
481             )
482         {
483             if ( !(*loop)->suspend( _bSuspend ) )
484             {
485                 if ( _bSuspend && ( loop != m_aSlaveHandlers.begin() ) )
486                 {
487                     // if we tried to suspend, but one of the slave handlers vetoed,
488                     // re-activate the handlers which actually did *not* veto
489                     // the suspension
490                     do
491                     {
492                         --loop;
493                         (*loop)->suspend( sal_False );
494                     }
495                     while ( loop != m_aSlaveHandlers.begin() );
496                 }
497                 return false;
498             }
499         }
500         return true;
501     }
502 
503     //--------------------------------------------------------------------
hasPropertyByName(const::rtl::OUString & _rName)504     sal_Bool SAL_CALL PropertyComposer::hasPropertyByName( const ::rtl::OUString& _rName ) throw (RuntimeException)
505     {
506         return impl_isSupportedProperty_nothrow( _rName );
507     }
508 
509 //........................................................................
510 } // namespace pcr
511 //........................................................................
512 
513