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_canvas.hxx"
30 
31 #include <osl/mutex.hxx>
32 #include <osl/process.h>
33 #include <cppuhelper/implementationentry.hxx>
34 #include <cppuhelper/factory.hxx>
35 #include <cppuhelper/implbase3.hxx>
36 #include <vcl/configsettings.hxx>
37 
38 #include <com/sun/star/uno/XComponentContext.hpp>
39 #include <com/sun/star/lang/XServiceInfo.hpp>
40 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
41 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
42 #include <com/sun/star/container/XNameAccess.hpp>
43 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
44 #include <com/sun/star/beans/PropertyValue.hpp>
45 
46 #include <boost/bind.hpp>
47 #include <vector>
48 #include <utility>
49 #include <functional>
50 #include <algorithm>
51 
52 #define OUSTR(x) ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(x) )
53 #define ARLEN(x) (sizeof (x) / sizeof *(x))
54 
55 
56 using namespace ::com::sun::star;
57 using namespace ::com::sun::star::uno;
58 using ::rtl::OUString;
59 
60 namespace
61 {
62 
63 OUString SAL_CALL getImplName()
64 {
65     return OUSTR("com.sun.star.comp.rendering.CanvasFactory");
66 }
67 
68 Sequence<OUString> SAL_CALL getSuppServices()
69 {
70     OUString name = OUSTR("com.sun.star.rendering.CanvasFactory");
71     return Sequence<OUString>(&name, 1);
72 }
73 
74 //==============================================================================
75 class CanvasFactory
76     : public ::cppu::WeakImplHelper3< lang::XServiceInfo,
77                                       lang::XMultiComponentFactory,
78                                       lang::XMultiServiceFactory >
79 {
80     typedef std::pair<OUString,Sequence<OUString> > AvailPair;
81     typedef std::pair<OUString,OUString>            CachePair;
82     typedef std::vector< AvailPair >                AvailVector;
83     typedef std::vector< CachePair >                CacheVector;
84 
85 
86     mutable ::osl::Mutex              m_mutex;
87     Reference<XComponentContext>      m_xContext;
88     Reference<container::XNameAccess> m_xCanvasConfigNameAccess;
89     AvailVector                       m_aAvailableImplementations;
90     AvailVector                       m_aAcceleratedImplementations;
91     AvailVector                       m_aAAImplementations;
92     mutable CacheVector               m_aCachedImplementations;
93     mutable bool                      m_bCacheHasForcedLastImpl;
94     mutable bool                      m_bCacheHasUseAcceleratedEntry;
95     mutable bool                      m_bCacheHasUseAAEntry;
96 
97     void checkConfigFlag( bool& r_bFlag,
98                           bool& r_CacheFlag,
99                           const OUString& nodeName ) const;
100     Reference<XInterface> use(
101         OUString const & serviceName,
102         Sequence<Any> const & args,
103         Reference<XComponentContext> const & xContext ) const;
104     Reference<XInterface> lookupAndUse(
105         OUString const & serviceName, Sequence<Any> const & args,
106         Reference<XComponentContext> const & xContext ) const;
107 
108 public:
109     virtual ~CanvasFactory();
110     CanvasFactory( Reference<XComponentContext> const & xContext );
111 
112     // XServiceInfo
113     virtual OUString SAL_CALL getImplementationName() throw (RuntimeException);
114     virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName )
115         throw (RuntimeException);
116     virtual Sequence<OUString> SAL_CALL getSupportedServiceNames()
117         throw (RuntimeException);
118 
119     // XMultiComponentFactory
120     virtual Sequence<OUString> SAL_CALL getAvailableServiceNames()
121         throw (RuntimeException);
122     virtual Reference<XInterface> SAL_CALL createInstanceWithContext(
123         OUString const & name,
124         Reference<XComponentContext> const & xContext ) throw (Exception);
125     virtual Reference<XInterface> SAL_CALL
126     createInstanceWithArgumentsAndContext(
127         OUString const & name,
128         Sequence<Any> const & args,
129         Reference<XComponentContext> const & xContext ) throw (Exception);
130 
131     // XMultiServiceFactory
132     virtual Reference<XInterface> SAL_CALL createInstance(
133         OUString const & name )
134         throw (Exception);
135     virtual Reference<XInterface> SAL_CALL createInstanceWithArguments(
136         OUString const & name, Sequence<Any> const & args )
137         throw (Exception);
138 };
139 
140 CanvasFactory::CanvasFactory( Reference<XComponentContext> const & xContext ) :
141     m_mutex(),
142     m_xContext(xContext),
143     m_xCanvasConfigNameAccess(),
144     m_aAvailableImplementations(),
145     m_aAcceleratedImplementations(),
146     m_aAAImplementations(),
147     m_aCachedImplementations(),
148     m_bCacheHasForcedLastImpl(),
149     m_bCacheHasUseAcceleratedEntry(),
150     m_bCacheHasUseAAEntry()
151 {
152     try
153     {
154         // read out configuration for preferred services:
155         Reference<lang::XMultiServiceFactory> xConfigProvider(
156             m_xContext->getServiceManager()->createInstanceWithContext(
157                 OUSTR("com.sun.star.configuration.ConfigurationProvider"),
158                 m_xContext ), UNO_QUERY_THROW );
159 
160         Any propValue(
161             makeAny( beans::PropertyValue(
162                          OUSTR("nodepath"), -1,
163                          makeAny( OUSTR("/org.openoffice.Office.Canvas") ),
164                          beans::PropertyState_DIRECT_VALUE ) ) );
165 
166         m_xCanvasConfigNameAccess.set(
167             xConfigProvider->createInstanceWithArguments(
168                 OUSTR("com.sun.star.configuration.ConfigurationAccess"),
169                 Sequence<Any>( &propValue, 1 ) ),
170             UNO_QUERY_THROW );
171 
172         propValue = makeAny(
173             beans::PropertyValue(
174                 OUSTR("nodepath"), -1,
175                 makeAny( OUSTR("/org.openoffice.Office.Canvas/CanvasServiceList") ),
176                 beans::PropertyState_DIRECT_VALUE ) );
177 
178         Reference<container::XNameAccess> xNameAccess(
179             xConfigProvider->createInstanceWithArguments(
180                 OUSTR("com.sun.star.configuration.ConfigurationAccess"),
181                 Sequence<Any>( &propValue, 1 ) ), UNO_QUERY_THROW );
182         Reference<container::XHierarchicalNameAccess> xHierarchicalNameAccess(
183             xNameAccess, UNO_QUERY_THROW);
184 
185         Sequence<OUString> serviceNames = xNameAccess->getElementNames();
186         const OUString* pCurr = serviceNames.getConstArray();
187         const OUString* const pEnd = pCurr + serviceNames.getLength();
188         while( pCurr != pEnd )
189         {
190             Reference<container::XNameAccess> xEntryNameAccess(
191                 xHierarchicalNameAccess->getByHierarchicalName(*pCurr),
192                 UNO_QUERY );
193 
194             if( xEntryNameAccess.is() )
195             {
196                 Sequence<OUString> implementationList;
197                 if( (xEntryNameAccess->getByName( OUSTR("PreferredImplementations") ) >>= implementationList) )
198                     m_aAvailableImplementations.push_back( std::make_pair(*pCurr,implementationList) );
199                 if( (xEntryNameAccess->getByName( OUSTR("AcceleratedImplementations") ) >>= implementationList) )
200                     m_aAcceleratedImplementations.push_back( std::make_pair(*pCurr,implementationList) );
201                 if( (xEntryNameAccess->getByName( OUSTR("AntialiasingImplementations") ) >>= implementationList) )
202                     m_aAAImplementations.push_back( std::make_pair(*pCurr,implementationList) );
203             }
204 
205             ++pCurr;
206         }
207     }
208     catch (RuntimeException &)
209     {
210         throw;
211     }
212     catch (Exception&)
213     {
214     }
215 
216     if( m_aAvailableImplementations.empty() )
217     {
218         // Ugh. Looks like configuration is borked. Fake minimal
219         // setup.
220         Sequence<OUString> aServices(1);
221         aServices[0] = OUSTR("com.sun.star.comp.rendering.Canvas.VCL");
222         m_aAvailableImplementations.push_back( std::make_pair(OUSTR("com.sun.star.rendering.Canvas"),
223                                                               aServices) );
224 
225         aServices[0] = OUSTR("com.sun.star.comp.rendering.SpriteCanvas.VCL");
226         m_aAvailableImplementations.push_back( std::make_pair(OUSTR("com.sun.star.rendering.SpriteCanvas"),
227                                                               aServices) );
228     }
229 }
230 
231 CanvasFactory::~CanvasFactory()
232 {
233 }
234 
235 //------------------------------------------------------------------------------
236 Reference<XInterface> create( Reference<XComponentContext> const & xContext )
237 {
238     return static_cast< ::cppu::OWeakObject * >(
239         new CanvasFactory( xContext ) );
240 }
241 
242 // XServiceInfo
243 //______________________________________________________________________________
244 OUString CanvasFactory::getImplementationName() throw (RuntimeException)
245 {
246     return getImplName();
247 }
248 
249 //______________________________________________________________________________
250 sal_Bool CanvasFactory::supportsService( OUString const & serviceName )
251     throw (RuntimeException)
252 {
253     return serviceName.equals(getSuppServices()[0]);
254 }
255 
256 //______________________________________________________________________________
257 Sequence<OUString> CanvasFactory::getSupportedServiceNames()
258     throw (RuntimeException)
259 {
260     return getSuppServices();
261 }
262 
263 // XMultiComponentFactory
264 //______________________________________________________________________________
265 Sequence<OUString> CanvasFactory::getAvailableServiceNames()
266     throw (RuntimeException)
267 {
268     Sequence<OUString> aServiceNames(m_aAvailableImplementations.size());
269     std::transform(m_aAvailableImplementations.begin(),
270                    m_aAvailableImplementations.end(),
271                    aServiceNames.getArray(),
272                    std::select1st<AvailPair>());
273     return aServiceNames;
274 }
275 
276 //______________________________________________________________________________
277 Reference<XInterface> CanvasFactory::createInstanceWithContext(
278     OUString const & name, Reference<XComponentContext> const & xContext )
279     throw (Exception)
280 {
281     return createInstanceWithArgumentsAndContext(
282         name, Sequence<Any>(), xContext );
283 }
284 
285 //______________________________________________________________________________
286 Reference<XInterface> CanvasFactory::use(
287     OUString const & serviceName,
288     Sequence<Any> const & args,
289     Reference<XComponentContext> const & xContext ) const
290 {
291     try {
292         return m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
293             serviceName, args, xContext);
294     }
295     catch (RuntimeException &)
296     {
297         throw;
298     }
299     catch (Exception &)
300     {
301         return Reference<XInterface>();
302     }
303 }
304 
305 //______________________________________________________________________________
306 void CanvasFactory::checkConfigFlag( bool& r_bFlag,
307                                      bool& r_CacheFlag,
308                                      const OUString& nodeName ) const
309 {
310     if( m_xCanvasConfigNameAccess.is() )
311     {
312         m_xCanvasConfigNameAccess->getByName( nodeName ) >>= r_bFlag;
313 
314         if( r_CacheFlag != r_bFlag )
315         {
316             // cache is invalid, because of different order of
317             // elements
318             r_CacheFlag = r_bFlag;
319             m_aCachedImplementations.clear();
320         }
321     }
322 }
323 
324 //______________________________________________________________________________
325 Reference<XInterface> CanvasFactory::lookupAndUse(
326     OUString const & serviceName, Sequence<Any> const & args,
327     Reference<XComponentContext> const & xContext ) const
328 {
329     ::osl::MutexGuard guard(m_mutex);
330 
331     // forcing last entry from impl list, if config flag set
332     bool bForceLastEntry(false);
333     checkConfigFlag( bForceLastEntry,
334                      m_bCacheHasForcedLastImpl,
335                      OUSTR("ForceSafeServiceImpl") );
336 
337     // use anti-aliasing canvas, if config flag set (or not existing)
338     bool bUseAAEntry(true);
339     checkConfigFlag( bUseAAEntry,
340                      m_bCacheHasUseAAEntry,
341                      OUSTR("UseAntialiasingCanvas") );
342 
343     // use accelerated canvas, if config flag set (or not existing)
344     bool bUseAcceleratedEntry(true);
345     checkConfigFlag( bUseAcceleratedEntry,
346                      m_bCacheHasUseAcceleratedEntry,
347                      OUSTR("UseAcceleratedCanvas") );
348 
349     // try to reuse last working implementation for given service name
350     const CacheVector::iterator aEnd(m_aCachedImplementations.end());
351     CacheVector::iterator aMatch;
352     if( (aMatch=std::find_if(m_aCachedImplementations.begin(),
353                              aEnd,
354                              boost::bind(&OUString::equals,
355                                          boost::cref(serviceName),
356                                          boost::bind(
357                                              std::select1st<CachePair>(),
358                                              _1)))) != aEnd )
359     {
360         Reference<XInterface> xCanvas( use( aMatch->second, args, xContext ) );
361         if(xCanvas.is())
362             return xCanvas;
363     }
364 
365     // lookup in available service list
366     const AvailVector::const_iterator aAvailEnd(m_aAvailableImplementations.end());
367     AvailVector::const_iterator aAvailImplsMatch;
368     if( (aAvailImplsMatch=std::find_if(m_aAvailableImplementations.begin(),
369                                        aAvailEnd,
370                                        boost::bind(&OUString::equals,
371                                                    boost::cref(serviceName),
372                                                    boost::bind(
373                                                        std::select1st<AvailPair>(),
374                                                        _1)))) == aAvailEnd )
375     {
376         return Reference<XInterface>();
377     }
378 
379     const AvailVector::const_iterator aAAEnd(m_aAAImplementations.end());
380     AvailVector::const_iterator aAAImplsMatch;
381     if( (aAAImplsMatch=std::find_if(m_aAAImplementations.begin(),
382                                     aAAEnd,
383                                     boost::bind(&OUString::equals,
384                                                 boost::cref(serviceName),
385                                                 boost::bind(
386                                                     std::select1st<AvailPair>(),
387                                                     _1)))) == aAAEnd )
388     {
389         return Reference<XInterface>();
390     }
391 
392     const AvailVector::const_iterator aAccelEnd(m_aAcceleratedImplementations.end());
393     AvailVector::const_iterator aAccelImplsMatch;
394     if( (aAccelImplsMatch=std::find_if(m_aAcceleratedImplementations.begin(),
395                                        aAccelEnd,
396                                        boost::bind(&OUString::equals,
397                                                    boost::cref(serviceName),
398                                                    boost::bind(
399                                                        std::select1st<AvailPair>(),
400                                                        _1)))) == aAccelEnd )
401     {
402         return Reference<XInterface>();
403     }
404 
405     const Sequence<OUString> aPreferredImpls( aAvailImplsMatch->second );
406     const OUString* pCurrImpl = aPreferredImpls.getConstArray();
407     const OUString* const pEndImpl = pCurrImpl + aPreferredImpls.getLength();
408 
409     const Sequence<OUString> aAAImpls( aAAImplsMatch->second );
410     const OUString* const pFirstAAImpl = aAAImpls.getConstArray();
411     const OUString* const pEndAAImpl = pFirstAAImpl + aAAImpls.getLength();
412 
413     const Sequence<OUString> aAccelImpls( aAccelImplsMatch->second );
414     const OUString* const pFirstAccelImpl = aAccelImpls.getConstArray();
415     const OUString* const pEndAccelImpl = pFirstAccelImpl + aAccelImpls.getLength();
416 
417     // force last entry from impl list, if config flag set
418     if( bForceLastEntry )
419         pCurrImpl = pEndImpl-1;
420 
421     while( pCurrImpl != pEndImpl )
422     {
423         const OUString aCurrName(pCurrImpl->trim());
424 
425         // check whether given canvas service is listed in the
426         // sequence of "accelerated canvas implementations"
427         const bool bIsAcceleratedImpl(
428             std::find_if(pFirstAccelImpl,
429                          pEndAccelImpl,
430                          boost::bind(&OUString::equals,
431                                      boost::cref(aCurrName),
432                                      boost::bind(
433                                          &OUString::trim,
434                                          _1))) != pEndAccelImpl );
435 
436         // check whether given canvas service is listed in the
437         // sequence of "antialiasing canvas implementations"
438         const bool bIsAAImpl(
439             std::find_if(pFirstAAImpl,
440                          pEndAAImpl,
441                          boost::bind(&OUString::equals,
442                                      boost::cref(aCurrName),
443                                      boost::bind(
444                                          &OUString::trim,
445                                          _1))) != pEndAAImpl );
446 
447         // try to instantiate canvas *only* if either accel and AA
448         // property match preference, *or*, if there's a mismatch, only
449         // go for a less capable canvas (that effectively let those
450         // pour canvas impls still work as fallbacks, should an
451         // accelerated/AA one fail). Property implies configuration:
452         // http://en.wikipedia.org/wiki/Truth_table#Logical_implication
453         if( (!bIsAAImpl || bUseAAEntry) && (!bIsAcceleratedImpl || bUseAcceleratedEntry) )
454         {
455             Reference<XInterface> xCanvas(
456                 use( pCurrImpl->trim(), args, xContext ) );
457 
458             if(xCanvas.is())
459             {
460                 if( aMatch != aEnd )
461                 {
462                     // cache entry exists, replace dysfunctional
463                     // implementation name
464                     aMatch->second = pCurrImpl->trim();
465                 }
466                 else
467                 {
468                     // new service name, add new cache entry
469                     m_aCachedImplementations.push_back(std::make_pair(serviceName,
470                                                                       pCurrImpl->trim()));
471                 }
472 
473                 return xCanvas;
474             }
475         }
476 
477         ++pCurrImpl;
478     }
479 
480     return Reference<XInterface>();
481 }
482 
483 //______________________________________________________________________________
484 Reference<XInterface> CanvasFactory::createInstanceWithArgumentsAndContext(
485     OUString const & preferredOne, Sequence<Any> const & args,
486     Reference<XComponentContext> const & xContext ) throw (Exception)
487 {
488     Reference<XInterface> xCanvas(
489         lookupAndUse( preferredOne, args, xContext ) );
490     if(xCanvas.is())
491         return xCanvas;
492 
493     // last resort: try service name directly
494     return use( preferredOne, args, xContext );
495 }
496 
497 // XMultiServiceFactory
498 //______________________________________________________________________________
499 Reference<XInterface> CanvasFactory::createInstance( OUString const & name )
500     throw (Exception)
501 {
502     return createInstanceWithArgumentsAndContext(
503         name, Sequence<Any>(), m_xContext );
504 }
505 
506 //______________________________________________________________________________
507 Reference<XInterface> CanvasFactory::createInstanceWithArguments(
508     OUString const & name, Sequence<Any> const & args ) throw (Exception)
509 {
510     return createInstanceWithArgumentsAndContext(
511         name, args, m_xContext );
512 }
513 
514 const ::cppu::ImplementationEntry s_entries [] = {
515     {
516         create,
517         getImplName,
518         getSuppServices,
519         ::cppu::createSingleComponentFactory,
520         0, 0
521     },
522     { 0, 0, 0, 0, 0, 0 }
523 };
524 
525 } // anon namespace
526 
527 extern "C" {
528 
529 void SAL_CALL component_getImplementationEnvironment(
530     const sal_Char ** ppEnvTypeName, uno_Environment ** /*ppEnv*/ )
531 {
532     *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
533 }
534 
535 void * SAL_CALL component_getFactory(
536     sal_Char const * pImplName,
537     lang::XMultiServiceFactory * pServiceManager,
538     registry::XRegistryKey * pRegistryKey )
539 {
540     return ::cppu::component_getFactoryHelper(
541         pImplName, pServiceManager, pRegistryKey, s_entries );
542 }
543 
544 }
545 
546