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_extensions.hxx"
30 
31 #ifndef EXTENSIONS_SOURCE_RESOURCE_OOORESOURCELOADER_CXX
32 #define EXTENSIONS_SOURCE_RESOURCE_OOORESOURCELOADER_CXX
33 #include "res_services.hxx"
34 
35 /** === begin UNO includes === **/
36 #include <com/sun/star/resource/XResourceBundleLoader.hpp>
37 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
38 #include <com/sun/star/uno/XComponentContext.hpp>
39 /** === end UNO includes === **/
40 #include <vcl/svapp.hxx>
41 #include <tools/simplerm.hxx>
42 #include <tools/rcid.h>
43 #include <cppuhelper/implbase1.hxx>
44 #include <cppuhelper/weakref.hxx>
45 
46 #include <boost/shared_ptr.hpp>
47 #include <map>
48 
49 //........................................................................
50 namespace res
51 {
52 //........................................................................
53 
54     /** === begin UNO using === **/
55     using ::com::sun::star::uno::Reference;
56     using ::com::sun::star::resource::XResourceBundleLoader;
57     using ::com::sun::star::resource::XResourceBundle;
58     using ::com::sun::star::resource::MissingResourceException;
59     using ::com::sun::star::uno::XComponentContext;
60     using ::com::sun::star::uno::Sequence;
61     using ::com::sun::star::uno::XInterface;
62     using ::com::sun::star::uno::RuntimeException;
63     using ::com::sun::star::lang::Locale;
64     using ::com::sun::star::uno::Any;
65     using ::com::sun::star::container::NoSuchElementException;
66     using ::com::sun::star::lang::WrappedTargetException;
67     using ::com::sun::star::uno::Type;
68     using ::com::sun::star::uno::WeakReference;
69     /** === end UNO using === **/
70 
71 	//====================================================================
72 	//= helper
73 	//====================================================================
74     typedef ::std::pair< ::rtl::OUString, Locale >  ResourceBundleDescriptor;
75 
76     struct ResourceBundleDescriptorLess : public ::std::binary_function< ResourceBundleDescriptor, ResourceBundleDescriptor, bool >
77     {
78         bool operator()( const ResourceBundleDescriptor& _lhs, const ResourceBundleDescriptor& _rhs ) const
79         {
80             if ( _lhs.first < _rhs.first )
81                 return true;
82             if ( _lhs.second.Language < _rhs.second.Language )
83                 return true;
84             if ( _lhs.second.Country < _rhs.second.Country )
85                 return true;
86             if ( _lhs.second.Variant < _rhs.second.Variant )
87                 return true;
88             return false;
89         }
90     };
91 
92 	//====================================================================
93 	//= OpenOfficeResourceLoader
94 	//====================================================================
95     typedef ::cppu::WeakImplHelper1 <   XResourceBundleLoader
96                                     >   OpenOfficeResourceLoader_Base;
97     class OpenOfficeResourceLoader : public OpenOfficeResourceLoader_Base
98 	{
99     private:
100         typedef ::std::map< ResourceBundleDescriptor, WeakReference< XResourceBundle >, ResourceBundleDescriptorLess >
101                                         ResourceBundleCache;
102 
103     private:
104         Reference< XComponentContext >  m_xContext;
105         ::osl::Mutex                    m_aMutex;
106         ResourceBundleCache             m_aBundleCache;
107 
108     protected:
109         OpenOfficeResourceLoader( const Reference< XComponentContext >& _rxContext );
110 
111     public:
112         static Sequence< ::rtl::OUString > getSupportedServiceNames_static();
113         static ::rtl::OUString  getImplementationName_static();
114         static ::rtl::OUString  getSingletonName_static();
115         static Reference< XInterface > Create( const Reference< XComponentContext >& _rxContext );
116 
117         // XResourceBundleLoader
118         virtual Reference< XResourceBundle > SAL_CALL loadBundle_Default( const ::rtl::OUString& aBaseName ) throw (MissingResourceException, RuntimeException);
119         virtual Reference< XResourceBundle > SAL_CALL loadBundle( const ::rtl::OUString& abaseName, const Locale& aLocale ) throw (MissingResourceException, RuntimeException);
120 
121     private:
122         OpenOfficeResourceLoader();                                             // never implemented
123         OpenOfficeResourceLoader( const OpenOfficeResourceLoader& );            // never implemented
124         OpenOfficeResourceLoader& operator=( const OpenOfficeResourceLoader& ); // never implemented
125 	};
126 
127 	//====================================================================
128 	//= IResourceType
129 	//====================================================================
130     /** encapsulates access to a fixed resource type
131     */
132     class IResourceType
133     {
134     public:
135         /** returns the RESOURCE_TYPE associated with this instance
136         */
137         virtual RESOURCE_TYPE getResourceType() const = 0;
138 
139         /** reads a single resource from the given resource manager
140             @param  _resourceManager
141                 the resource manager to read from
142             @param  _resourceId
143                 the id of the resource to read
144             @return
145                 the required resource
146             @precond
147                 the caler checked via <code>_resourceManager.IsAvailable( getResourceType(), _resourceId )</code>
148                 that the required resource really exists
149         */
150         virtual Any getResource( SimpleResMgr& _resourceManager, sal_Int32 _resourceId ) const = 0;
151 
152         virtual ~IResourceType() { };
153     };
154 
155 	//====================================================================
156 	//= StringResourceAccess
157 	//====================================================================
158     class StringResourceAccess : public IResourceType
159     {
160     public:
161         StringResourceAccess();
162 
163         // IResourceType
164         virtual RESOURCE_TYPE getResourceType() const;
165         virtual Any getResource( SimpleResMgr& _resourceManager, sal_Int32 _resourceId ) const;
166     };
167 
168     //--------------------------------------------------------------------
169     StringResourceAccess::StringResourceAccess()
170     {
171     }
172 
173     //--------------------------------------------------------------------
174     RESOURCE_TYPE StringResourceAccess::getResourceType() const
175     {
176         return RSC_STRING;
177     }
178 
179     //--------------------------------------------------------------------
180     Any StringResourceAccess::getResource( SimpleResMgr& _resourceManager, sal_Int32 _resourceId ) const
181     {
182         OSL_PRECOND( _resourceManager.IsAvailable( getResourceType(), _resourceId ), "StringResourceAccess::getResource: precondition not met!" );
183         Any aResource;
184         aResource <<= ::rtl::OUString( _resourceManager.ReadString( _resourceId ) );
185         return aResource;
186     }
187 
188     //====================================================================
189 	//= OpenOfficeResourceBundle
190 	//====================================================================
191     typedef ::cppu::WeakImplHelper1 <   XResourceBundle
192                                     >   OpenOfficeResourceBundle_Base;
193     class OpenOfficeResourceBundle : public OpenOfficeResourceBundle_Base
194     {
195     private:
196         typedef ::boost::shared_ptr< IResourceType >            ResourceTypePtr;
197         typedef ::std::map< ::rtl::OUString, ResourceTypePtr >  ResourceTypes;
198 
199         ::osl::Mutex                    m_aMutex;
200         Reference< XResourceBundle >    m_xParent;
201         Locale                          m_aLocale;
202         SimpleResMgr*                   m_pResourceManager;
203         ResourceTypes                   m_aResourceTypes;
204 
205     public:
206         OpenOfficeResourceBundle(
207             const Reference< XComponentContext >& _rxContext,
208             const ::rtl::OUString& _rBaseName,
209             const Locale& _rLocale
210         );
211 
212     protected:
213         ~OpenOfficeResourceBundle();
214 
215     public:
216         // XResourceBundle
217         virtual ::com::sun::star::uno::Reference< ::com::sun::star::resource::XResourceBundle > SAL_CALL getParent() throw (::com::sun::star::uno::RuntimeException);
218         virtual void SAL_CALL setParent( const ::com::sun::star::uno::Reference< ::com::sun::star::resource::XResourceBundle >& _parent ) throw (::com::sun::star::uno::RuntimeException);
219         virtual ::com::sun::star::lang::Locale SAL_CALL getLocale(  ) throw (::com::sun::star::uno::RuntimeException);
220         virtual ::com::sun::star::uno::Any SAL_CALL getDirectElement( const ::rtl::OUString& key ) throw (::com::sun::star::uno::RuntimeException);
221 
222         // XNameAccess (base of XResourceBundle)
223         virtual ::com::sun::star::uno::Any SAL_CALL getByName( const ::rtl::OUString& aName ) throw (::com::sun::star::container::NoSuchElementException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException);
224         virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getElementNames(  ) throw (::com::sun::star::uno::RuntimeException);
225         virtual ::sal_Bool SAL_CALL hasByName( const ::rtl::OUString& aName ) throw (::com::sun::star::uno::RuntimeException);
226 
227         // XElementAccess (base of XNameAccess)
228         virtual ::com::sun::star::uno::Type SAL_CALL getElementType(  ) throw (::com::sun::star::uno::RuntimeException);
229         virtual ::sal_Bool SAL_CALL hasElements(  ) throw (::com::sun::star::uno::RuntimeException);
230 
231     private:
232         /** retrievs the element with the given key, without asking our parent bundle
233             @param  _key
234                 the key of the element to retrieve
235             @param  _out_Element
236                 will contained the retrieved element upon successful return. If the method is unsuccessful, the
237                 value will not be touched.
238             @return
239                 <TRUE/> if and only if the element could be retrieved
240             @precond
241                 our mutex is locked
242         */
243         bool    impl_getDirectElement_nothrow( const ::rtl::OUString& _key, Any& _out_Element ) const;
244 
245         /** retrieves the resource type and id from a given resource key, which assembles those two
246             @param  _key
247                 the resource key as got via a public API call
248             @param  _out_resourceType
249                 the resource type, if successful
250             @param  _out_resourceId
251                 the resource id, if successful
252             @return
253                 <TRUE/> if and only if the given key specifies a known resource type, and contains a valid
254                 resource id
255         */
256         bool    impl_getResourceTypeAndId_nothrow( const ::rtl::OUString& _key, ResourceTypePtr& _out_resourceType, sal_Int32& _out_resourceId ) const;
257     };
258 
259 	//====================================================================
260 	//= OpenOfficeResourceLoader
261 	//====================================================================
262 	//--------------------------------------------------------------------
263     OpenOfficeResourceLoader::OpenOfficeResourceLoader( const Reference< XComponentContext >& _rxContext )
264         :m_xContext( _rxContext )
265     {
266     }
267 
268 	//--------------------------------------------------------------------
269     Sequence< ::rtl::OUString > OpenOfficeResourceLoader::getSupportedServiceNames_static()
270     {
271         Sequence< ::rtl::OUString > aServices( 1 );
272         aServices[ 0 ] = getSingletonName_static();
273         return aServices;
274     }
275 
276 	//--------------------------------------------------------------------
277     ::rtl::OUString OpenOfficeResourceLoader::getImplementationName_static()
278     {
279         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.resource.OpenOfficeResourceLoader" ) );
280     }
281 
282 	//--------------------------------------------------------------------
283     ::rtl::OUString OpenOfficeResourceLoader::getSingletonName_static()
284     {
285         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.resource.OfficeResourceLoader" ) );
286     }
287 
288 	//--------------------------------------------------------------------
289     Reference< XInterface > OpenOfficeResourceLoader::Create( const Reference< XComponentContext >& _rxContext )
290     {
291         return *( new OpenOfficeResourceLoader( _rxContext ) );
292     }
293 
294     //--------------------------------------------------------------------
295     Reference< XResourceBundle > SAL_CALL OpenOfficeResourceLoader::loadBundle_Default( const ::rtl::OUString& _baseName ) throw (MissingResourceException, RuntimeException)
296     {
297         return loadBundle( _baseName, Application::GetSettings().GetUILocale() );
298     }
299 
300     //--------------------------------------------------------------------
301     Reference< XResourceBundle > SAL_CALL OpenOfficeResourceLoader::loadBundle( const ::rtl::OUString& _baseName, const Locale& _locale ) throw (MissingResourceException, RuntimeException)
302     {
303         ::osl::MutexGuard aGuard( m_aMutex );
304 
305         Reference< XResourceBundle > xBundle;
306 
307         ResourceBundleDescriptor resourceDescriptor( _baseName, _locale );
308         ResourceBundleCache::iterator cachePos = m_aBundleCache.find( resourceDescriptor );
309         if ( cachePos != m_aBundleCache.end() )
310             xBundle = cachePos->second;
311 
312         if ( !xBundle.is() )
313         {   // not in the cache, or already died
314             xBundle = new OpenOfficeResourceBundle( m_xContext, _baseName, _locale );
315             m_aBundleCache.insert( ResourceBundleCache::value_type( resourceDescriptor, xBundle ) );
316         }
317 
318         return xBundle;
319     }
320 
321     //--------------------------------------------------------------------
322     ComponentInfo getComponentInfo_OpenOfficeResourceLoader()
323     {
324         ComponentInfo aInfo;
325         aInfo.aSupportedServices = OpenOfficeResourceLoader::getSupportedServiceNames_static();
326         aInfo.sImplementationName = OpenOfficeResourceLoader::getImplementationName_static();
327         aInfo.sSingletonName = OpenOfficeResourceLoader::getSingletonName_static();
328         aInfo.pFactory = &OpenOfficeResourceLoader::Create;
329         return aInfo;
330     }
331 
332 	//====================================================================
333 	//= OpenOfficeResourceBundle
334 	//====================================================================
335     //--------------------------------------------------------------------
336     OpenOfficeResourceBundle::OpenOfficeResourceBundle( const Reference< XComponentContext >& /*_rxContext*/, const ::rtl::OUString& _rBaseName, const Locale& _rLocale )
337         :m_aLocale( _rLocale )
338         ,m_pResourceManager( NULL )
339     {
340         ::rtl::OUString sBaseName( _rBaseName );
341         m_pResourceManager = new SimpleResMgr( sBaseName, m_aLocale );
342 
343         if ( !m_pResourceManager->IsValid() )
344         {
345             delete m_pResourceManager, m_pResourceManager = NULL;
346             throw MissingResourceException();
347         }
348 
349         // supported resource types so far: strings
350         m_aResourceTypes[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "string" ) ) ] =
351             ResourceTypePtr( new StringResourceAccess );
352     }
353 
354     //--------------------------------------------------------------------
355     OpenOfficeResourceBundle::~OpenOfficeResourceBundle()
356     {
357         delete m_pResourceManager;
358     }
359 
360     //--------------------------------------------------------------------
361     Reference< XResourceBundle > SAL_CALL OpenOfficeResourceBundle::getParent() throw (RuntimeException)
362     {
363         ::osl::MutexGuard aGuard( m_aMutex );
364         return m_xParent;
365     }
366 
367     //--------------------------------------------------------------------
368     void SAL_CALL OpenOfficeResourceBundle::setParent( const Reference< XResourceBundle >& _parent ) throw (RuntimeException)
369     {
370         ::osl::MutexGuard aGuard( m_aMutex );
371         m_xParent = _parent;
372     }
373 
374     //--------------------------------------------------------------------
375     Locale SAL_CALL OpenOfficeResourceBundle::getLocale(  ) throw (RuntimeException)
376     {
377         ::osl::MutexGuard aGuard( m_aMutex );
378         return m_aLocale;
379     }
380 
381     //--------------------------------------------------------------------
382     bool OpenOfficeResourceBundle::impl_getResourceTypeAndId_nothrow( const ::rtl::OUString& _key, ResourceTypePtr& _out_resourceType, sal_Int32& _out_resourceId ) const
383     {
384         sal_Int32 typeSeparatorPos = _key.indexOf( ':' );
385         if ( typeSeparatorPos == -1 )
386             // invalid key
387             return false;
388 
389         ::rtl::OUString resourceType = _key.copy( 0, typeSeparatorPos );
390 
391         ResourceTypes::const_iterator typePos = m_aResourceTypes.find( resourceType );
392         if ( typePos == m_aResourceTypes.end() )
393             // don't know this resource type
394             return false;
395 
396         _out_resourceType = typePos->second;
397         _out_resourceId = _key.copy( typeSeparatorPos + 1 ).toInt32();
398         return true;
399     }
400 
401     //--------------------------------------------------------------------
402     bool OpenOfficeResourceBundle::impl_getDirectElement_nothrow( const ::rtl::OUString& _key, Any& _out_Element ) const
403     {
404         ResourceTypePtr resourceType;
405         sal_Int32 resourceId( 0 );
406         if ( !impl_getResourceTypeAndId_nothrow( _key, resourceType, resourceId ) )
407             return false;
408 
409         if ( !m_pResourceManager->IsAvailable( resourceType->getResourceType(), resourceId ) )
410             // no such resource with the given type/id
411             return false;
412 
413         _out_Element = resourceType->getResource( *m_pResourceManager, resourceId );
414         return _out_Element.hasValue();
415     }
416 
417     //--------------------------------------------------------------------
418     Any SAL_CALL OpenOfficeResourceBundle::getDirectElement( const ::rtl::OUString& _key ) throw (RuntimeException)
419     {
420         ::osl::MutexGuard aGuard( m_aMutex );
421 
422         Any aElement;
423         impl_getDirectElement_nothrow( _key, aElement );
424         return aElement;
425     }
426 
427     //--------------------------------------------------------------------
428     Any SAL_CALL OpenOfficeResourceBundle::getByName( const ::rtl::OUString& _key ) throw (NoSuchElementException, WrappedTargetException, RuntimeException)
429     {
430         ::osl::MutexGuard aGuard( m_aMutex );
431 
432         Any aElement;
433         if ( !impl_getDirectElement_nothrow( _key, aElement ) )
434         {
435             if ( m_xParent.is() )
436                 aElement = m_xParent->getByName( _key );
437         }
438 
439         if ( !aElement.hasValue() )
440             throw NoSuchElementException( ::rtl::OUString(), *this );
441 
442         return aElement;
443     }
444 
445     //--------------------------------------------------------------------
446     Sequence< ::rtl::OUString > SAL_CALL OpenOfficeResourceBundle::getElementNames(  ) throw (RuntimeException)
447     {
448         ::osl::MutexGuard aGuard( m_aMutex );
449         OSL_ENSURE( false, "OpenOfficeResourceBundle::getElementNames: not implemented!" );
450             // the (Simple)ResManager does not provide an API to enumerate the resources
451         return Sequence< ::rtl::OUString >( );
452     }
453 
454     //--------------------------------------------------------------------
455     ::sal_Bool SAL_CALL OpenOfficeResourceBundle::hasByName( const ::rtl::OUString& _key ) throw (RuntimeException)
456     {
457         ::osl::MutexGuard aGuard( m_aMutex );
458 
459         ResourceTypePtr resourceType;
460         sal_Int32 resourceId( 0 );
461         if ( !impl_getResourceTypeAndId_nothrow( _key, resourceType, resourceId ) )
462             return sal_False;
463 
464         if ( !m_pResourceManager->IsAvailable( resourceType->getResourceType(), resourceId ) )
465             return sal_False;
466 
467         return sal_True;
468     }
469 
470     //--------------------------------------------------------------------
471     Type SAL_CALL OpenOfficeResourceBundle::getElementType(  ) throw (RuntimeException)
472     {
473         return ::cppu::UnoType< Any >::get();
474     }
475 
476     //--------------------------------------------------------------------
477     ::sal_Bool SAL_CALL OpenOfficeResourceBundle::hasElements(  ) throw (RuntimeException)
478     {
479         ::osl::MutexGuard aGuard( m_aMutex );
480         OSL_ENSURE( false, "OpenOfficeResourceBundle::hasElements: not implemented!" );
481             // the (Simple)ResManager does not provide an API to enumerate the resources
482         return ::sal_Bool( );
483     }
484 
485 //........................................................................
486 } // namespace res
487 //........................................................................
488 
489 #endif // EXTENSIONS_SOURCE_RESOURCE_OOORESOURCELOADER_CXX
490