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 #include "precompiled_configmgr.hxx"
29 #include "sal/config.h"
30 
31 #include <vector>
32 
33 #include "boost/noncopyable.hpp"
34 #include "com/sun/star/beans/NamedValue.hpp"
35 #include "com/sun/star/beans/PropertyValue.hpp"
36 #include "com/sun/star/lang/EventObject.hpp"
37 #include "com/sun/star/lang/Locale.hpp"
38 #include "com/sun/star/lang/XLocalizable.hpp"
39 #include "com/sun/star/lang/XMultiServiceFactory.hpp"
40 #include "com/sun/star/lang/XServiceInfo.hpp"
41 #include "com/sun/star/lang/XSingleComponentFactory.hpp"
42 #include "com/sun/star/uno/Any.hxx"
43 #include "com/sun/star/uno/DeploymentException.hpp"
44 #include "com/sun/star/uno/Exception.hpp"
45 #include "com/sun/star/uno/Reference.hxx"
46 #include "com/sun/star/uno/RuntimeException.hpp"
47 #include "com/sun/star/uno/Sequence.hxx"
48 #include "com/sun/star/uno/XComponentContext.hpp"
49 #include "com/sun/star/uno/XInterface.hpp"
50 #include "com/sun/star/util/XFlushListener.hpp"
51 #include "com/sun/star/util/XFlushable.hpp"
52 #include "com/sun/star/util/XRefreshListener.hpp"
53 #include "com/sun/star/util/XRefreshable.hpp"
54 #include "comphelper/locale.hxx"
55 #include "cppu/unotype.hxx"
56 #include "cppuhelper/compbase5.hxx"
57 #include "cppuhelper/factory.hxx"
58 #include "cppuhelper/implbase2.hxx"
59 #include "cppuhelper/interfacecontainer.hxx"
60 #include "cppuhelper/weak.hxx"
61 #include "osl/diagnose.h"
62 #include "osl/mutex.hxx"
63 #include "sal/types.h"
64 #include "rtl/ref.hxx"
65 #include "rtl/unload.h"
66 #include "rtl/ustring.h"
67 #include "rtl/ustring.hxx"
68 
69 #include "components.hxx"
70 #include "configurationprovider.hxx"
71 #include "lock.hxx"
72 #include "rootaccess.hxx"
73 
74 namespace configmgr { namespace configuration_provider {
75 
76 namespace {
77 
78 namespace css = com::sun::star;
79 
80 char const accessServiceName[] =
81     "com.sun.star.configuration.ConfigurationAccess";
82 char const updateAccessServiceName[] =
83     "com.sun.star.configuration.ConfigurationUpdateAccess";
84 
85 void badNodePath() {
86     throw css::uno::Exception(
87         rtl::OUString(
88             RTL_CONSTASCII_USTRINGPARAM(
89                 "com.sun.star.configuration.ConfigurationProvider expects a"
90                 " single, non-empty, string nodepath argument")),
91         0);
92 }
93 
94 typedef
95     cppu::WeakComponentImplHelper5<
96         css::lang::XServiceInfo, css::lang::XMultiServiceFactory,
97         css::util::XRefreshable, css::util::XFlushable,
98         css::lang::XLocalizable >
99     ServiceBase;
100 
101 class Service:
102     private osl::Mutex, public ServiceBase, private boost::noncopyable
103 {
104 public:
105     Service(
106         css::uno::Reference< css::uno::XComponentContext > const context,
107         rtl::OUString const & locale):
108         ServiceBase(*static_cast< osl::Mutex * >(this)), context_(context),
109         locale_(locale)
110     {
111         OSL_ASSERT(context.is());
112     }
113 
114 private:
115     virtual ~Service() {}
116 
117     virtual void SAL_CALL disposing() { flushModifications(); }
118 
119     virtual rtl::OUString SAL_CALL getImplementationName()
120         throw (css::uno::RuntimeException)
121     { return configuration_provider::getImplementationName(); }
122 
123     virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName)
124         throw (css::uno::RuntimeException)
125     { return ServiceName == getSupportedServiceNames()[0]; } //TODO
126 
127     virtual css::uno::Sequence< rtl::OUString > SAL_CALL
128     getSupportedServiceNames() throw (css::uno::RuntimeException)
129     { return configuration_provider::getSupportedServiceNames(); }
130 
131     virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance(
132         rtl::OUString const & aServiceSpecifier)
133         throw (css::uno::Exception, css::uno::RuntimeException);
134 
135     virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
136     createInstanceWithArguments(
137         rtl::OUString const & ServiceSpecifier,
138         css::uno::Sequence< css::uno::Any > const & Arguments)
139         throw (css::uno::Exception, css::uno::RuntimeException);
140 
141     virtual css::uno::Sequence< rtl::OUString > SAL_CALL
142     getAvailableServiceNames() throw (css::uno::RuntimeException);
143 
144     virtual void SAL_CALL refresh() throw (css::uno::RuntimeException);
145 
146     virtual void SAL_CALL addRefreshListener(
147         css::uno::Reference< css::util::XRefreshListener > const & l)
148         throw (css::uno::RuntimeException);
149 
150     virtual void SAL_CALL removeRefreshListener(
151         css::uno::Reference< css::util::XRefreshListener > const & l)
152         throw (css::uno::RuntimeException);
153 
154     virtual void SAL_CALL flush() throw (css::uno::RuntimeException);
155 
156     virtual void SAL_CALL addFlushListener(
157         css::uno::Reference< css::util::XFlushListener > const & l)
158         throw (css::uno::RuntimeException);
159 
160     virtual void SAL_CALL removeFlushListener(
161         css::uno::Reference< css::util::XFlushListener > const & l)
162         throw (css::uno::RuntimeException);
163 
164     virtual void SAL_CALL setLocale(css::lang::Locale const & eLocale)
165         throw (css::uno::RuntimeException);
166 
167     virtual css::lang::Locale SAL_CALL getLocale()
168         throw (css::uno::RuntimeException);
169 
170     void flushModifications() const;
171 
172     css::uno::Reference< css::uno::XComponentContext > context_;
173     rtl::OUString locale_;
174 };
175 
176 css::uno::Reference< css::uno::XInterface > Service::createInstance(
177     rtl::OUString const & aServiceSpecifier)
178     throw (css::uno::Exception, css::uno::RuntimeException)
179 {
180     return createInstanceWithArguments(
181         aServiceSpecifier, css::uno::Sequence< css::uno::Any >());
182 }
183 
184 css::uno::Reference< css::uno::XInterface >
185 Service::createInstanceWithArguments(
186     rtl::OUString const & ServiceSpecifier,
187     css::uno::Sequence< css::uno::Any > const & Arguments)
188     throw (css::uno::Exception, css::uno::RuntimeException)
189 {
190     rtl::OUString nodepath;
191     rtl::OUString locale;
192     for (sal_Int32 i = 0; i < Arguments.getLength(); ++i) {
193         css::beans::NamedValue v1;
194         css::beans::PropertyValue v2;
195         rtl::OUString name;
196         css::uno::Any value;
197         if (Arguments[i] >>= v1) {
198             name = v1.Name;
199             value = v1.Value;
200         } else if (Arguments[i] >>= v2) {
201             name = v2.Name;
202             value = v2.Value;
203         } else if (Arguments.getLength() == 1 && (Arguments[i] >>= nodepath)) {
204             // For backwards compatibility, allow a single string argument that
205             // denotes nodepath.
206             if (nodepath.getLength() == 0) {
207                 badNodePath();
208             }
209             break;
210         } else {
211             throw css::uno::Exception(
212                 rtl::OUString(
213                     RTL_CONSTASCII_USTRINGPARAM(
214                         "com.sun.star.configuration.ConfigurationProvider"
215                         " expects NamedValue or PropertyValue arguments")),
216                 0);
217         }
218         // For backwards compatibility, allow "nodepath" and "Locale" in any
219         // case:
220         if (name.equalsIgnoreAsciiCaseAsciiL(
221                 RTL_CONSTASCII_STRINGPARAM("nodepath")))
222         {
223             if (nodepath.getLength() != 0 || !(value >>= nodepath) ||
224                 nodepath.getLength() == 0)
225             {
226                 badNodePath();
227             }
228         } else if (name.equalsIgnoreAsciiCaseAsciiL(
229                        RTL_CONSTASCII_STRINGPARAM("locale")))
230         {
231             if (locale.getLength() != 0 || !(value >>= locale) ||
232                 locale.getLength() == 0)
233             {
234                 throw css::uno::Exception(
235                     rtl::OUString(
236                         RTL_CONSTASCII_USTRINGPARAM(
237                             "com.sun.star.configuration.ConfigurationProvider"
238                             " expects at most one, non-empty, string Locale"
239                             " argument")),
240                     0);
241             }
242         }
243     }
244     if (nodepath.getLength() == 0) {
245         badNodePath();
246     }
247     // For backwards compatibility, allow a nodepath that misses the leading
248     // slash:
249     if (nodepath[0] != '/') {
250         nodepath = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/")) + nodepath;
251     }
252     if (locale.getLength() == 0) {
253         //TODO: should the Access use the dynamically changing locale_ instead?
254         locale = locale_;
255         if (locale.getLength() == 0) {
256             locale = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en-US"));
257         }
258     }
259     bool update;
260     if (ServiceSpecifier.equalsAsciiL(
261             RTL_CONSTASCII_STRINGPARAM(accessServiceName)))
262     {
263         update = false;
264     } else if (ServiceSpecifier.equalsAsciiL(
265                    RTL_CONSTASCII_STRINGPARAM(updateAccessServiceName)))
266     {
267         update = true;
268     } else {
269         throw css::uno::Exception(
270             (rtl::OUString(
271                 RTL_CONSTASCII_USTRINGPARAM(
272                     "com.sun.star.configuration.ConfigurationProvider does not"
273                     " support service ")) +
274              ServiceSpecifier),
275             static_cast< cppu::OWeakObject * >(this));
276     }
277     osl::MutexGuard guard(lock);
278     Components & components = Components::getSingleton(context_);
279     rtl::Reference< RootAccess > root(
280         new RootAccess(components, nodepath, locale, update));
281     if (root->isValue()) {
282         throw css::uno::Exception(
283             (rtl::OUString(
284                 RTL_CONSTASCII_USTRINGPARAM(
285                     "com.sun.star.configuration.ConfigurationProvider: there is"
286                     " a leaf value at nodepath ")) +
287              nodepath),
288             static_cast< cppu::OWeakObject * >(this));
289     }
290     components.addRootAccess(root);
291     return static_cast< cppu::OWeakObject * >(root.get());
292 }
293 
294 css::uno::Sequence< rtl::OUString > Service::getAvailableServiceNames()
295     throw (css::uno::RuntimeException)
296 {
297     css::uno::Sequence< rtl::OUString > names(2);
298     names[0] = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(accessServiceName));
299     names[1] = rtl::OUString(
300         RTL_CONSTASCII_USTRINGPARAM(updateAccessServiceName));
301     return names;
302 }
303 
304 void Service::refresh() throw (css::uno::RuntimeException) {
305     //TODO
306     cppu::OInterfaceContainerHelper * cont = rBHelper.getContainer(
307         cppu::UnoType< css::util::XRefreshListener >::get());
308     if (cont != 0) {
309         css::lang::EventObject ev(static_cast< cppu::OWeakObject * >(this));
310         cont->notifyEach(&css::util::XRefreshListener::refreshed, ev);
311     }
312 }
313 
314 void Service::addRefreshListener(
315     css::uno::Reference< css::util::XRefreshListener > const & l)
316     throw (css::uno::RuntimeException)
317 {
318     rBHelper.addListener(
319         cppu::UnoType< css::util::XRefreshListener >::get(), l);
320 }
321 
322 void Service::removeRefreshListener(
323     css::uno::Reference< css::util::XRefreshListener > const & l)
324     throw (css::uno::RuntimeException)
325 {
326     rBHelper.removeListener(
327         cppu::UnoType< css::util::XRefreshListener >::get(), l);
328 }
329 
330 void Service::flush() throw (css::uno::RuntimeException) {
331     flushModifications();
332     cppu::OInterfaceContainerHelper * cont = rBHelper.getContainer(
333         cppu::UnoType< css::util::XFlushListener >::get());
334     if (cont != 0) {
335         css::lang::EventObject ev(static_cast< cppu::OWeakObject * >(this));
336         cont->notifyEach(&css::util::XFlushListener::flushed, ev);
337     }
338 }
339 
340 void Service::addFlushListener(
341     css::uno::Reference< css::util::XFlushListener > const & l)
342     throw (css::uno::RuntimeException)
343 {
344     rBHelper.addListener(cppu::UnoType< css::util::XFlushListener >::get(), l);
345 }
346 
347 void Service::removeFlushListener(
348     css::uno::Reference< css::util::XFlushListener > const & l)
349     throw (css::uno::RuntimeException)
350 {
351     rBHelper.removeListener(
352         cppu::UnoType< css::util::XFlushListener >::get(), l);
353 }
354 
355 void Service::setLocale(css::lang::Locale const & eLocale)
356     throw (css::uno::RuntimeException)
357 {
358     osl::MutexGuard guard(lock);
359     locale_ = comphelper::Locale(
360         eLocale.Language, eLocale.Country, eLocale.Variant).toISO();
361 }
362 
363 css::lang::Locale Service::getLocale() throw (css::uno::RuntimeException) {
364     osl::MutexGuard guard(lock);
365     css::lang::Locale loc;
366     if (locale_.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("*"))) {
367         loc.Language = locale_;
368     } else if (locale_.getLength() != 0) {
369         try {
370             comphelper::Locale l(locale_);
371             loc.Language = l.getLanguage();
372             loc.Country = l.getCountry();
373             loc.Variant = l.getVariant();
374         } catch (comphelper::Locale::MalFormedLocaleException & e) {
375             throw css::uno::RuntimeException(
376                 (rtl::OUString(
377                     RTL_CONSTASCII_USTRINGPARAM("MalformedLocaleException: ")) +
378                  e.Message),
379                 static_cast< cppu::OWeakObject * >(this));
380         }
381     }
382     return loc;
383 }
384 
385 void Service::flushModifications() const {
386     Components * components;
387     {
388         osl::MutexGuard guard(lock);
389         components = &Components::getSingleton(context_);
390     }
391     components->flushModifications();
392 }
393 
394 class Factory:
395     public cppu::WeakImplHelper2<
396         css::lang::XSingleComponentFactory, css::lang::XServiceInfo >,
397     private boost::noncopyable
398 {
399 public:
400     Factory() {}
401 
402 private:
403     virtual ~Factory() {}
404 
405     virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
406     createInstanceWithContext(
407         css::uno::Reference< css::uno::XComponentContext > const & Context)
408         throw (css::uno::Exception, css::uno::RuntimeException);
409 
410     virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
411     createInstanceWithArgumentsAndContext(
412         css::uno::Sequence< css::uno::Any > const & Arguments,
413         css::uno::Reference< css::uno::XComponentContext > const & Context)
414         throw (css::uno::Exception, css::uno::RuntimeException);
415 
416     virtual rtl::OUString SAL_CALL getImplementationName()
417         throw (css::uno::RuntimeException)
418     { return configuration_provider::getImplementationName(); }
419 
420     virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName)
421         throw (css::uno::RuntimeException)
422     { return ServiceName == getSupportedServiceNames()[0]; } //TODO
423 
424     virtual css::uno::Sequence< rtl::OUString > SAL_CALL
425     getSupportedServiceNames() throw (css::uno::RuntimeException)
426     { return configuration_provider::getSupportedServiceNames(); }
427 };
428 
429 css::uno::Reference< css::uno::XInterface > Factory::createInstanceWithContext(
430     css::uno::Reference< css::uno::XComponentContext > const & Context)
431     throw (css::uno::Exception, css::uno::RuntimeException)
432 {
433     return createInstanceWithArgumentsAndContext(
434         css::uno::Sequence< css::uno::Any >(), Context);
435 }
436 
437 css::uno::Reference< css::uno::XInterface >
438 Factory::createInstanceWithArgumentsAndContext(
439     css::uno::Sequence< css::uno::Any > const & Arguments,
440     css::uno::Reference< css::uno::XComponentContext > const & Context)
441     throw (css::uno::Exception, css::uno::RuntimeException)
442 {
443     if (Arguments.getLength() == 0) {
444         css::uno::Reference< css::uno::XInterface > instance;
445         if (!(Context->getValueByName(
446                   rtl::OUString(
447                       RTL_CONSTASCII_USTRINGPARAM(
448                           "/singletons/"
449                           "com.sun.star.configuration.theDefaultProvider")))
450               >>= instance) ||
451             !instance.is())
452         {
453             throw css::uno::DeploymentException(
454                 rtl::OUString(
455                     RTL_CONSTASCII_USTRINGPARAM(
456                         "component context fails to supply singleton"
457                         " com.sun.star.configuration.theDefaultProvider")),
458                 Context);
459         }
460         return instance;
461     } else {
462         rtl::OUString locale;
463         for (sal_Int32 i = 0; i < Arguments.getLength(); ++i) {
464             css::beans::NamedValue v1;
465             css::beans::PropertyValue v2;
466             rtl::OUString name;
467             css::uno::Any value;
468             if (Arguments[i] >>= v1) {
469                 name = v1.Name;
470                 value = v1.Value;
471             } else if (Arguments[i] >>= v2) {
472                 name = v2.Name;
473                 value = v2.Value;
474             } else {
475                 throw css::uno::Exception(
476                     rtl::OUString(
477                         RTL_CONSTASCII_USTRINGPARAM(
478                             "com.sun.star.configuration.ConfigurationProvider"
479                             " factory expects NamedValue or PropertyValue"
480                             " arguments")),
481                     0);
482             }
483             // For backwards compatibility, allow "Locale" and (ignored)
484             // "EnableAsync" in any case:
485             if (name.equalsIgnoreAsciiCaseAsciiL(
486                     RTL_CONSTASCII_STRINGPARAM("locale")))
487             {
488                 if (locale.getLength() != 0 || !(value >>= locale) ||
489                     locale.getLength() == 0)
490                 {
491                     throw css::uno::Exception(
492                         rtl::OUString(
493                             RTL_CONSTASCII_USTRINGPARAM(
494                                 "com.sun.star.configuration."
495                                 "ConfigurationProvider factory expects at most"
496                                 " one, non-empty, string Locale argument")),
497                         0);
498                 }
499             } else if (!name.equalsIgnoreAsciiCaseAsciiL(
500                            RTL_CONSTASCII_STRINGPARAM("enableasync")))
501             {
502                 throw css::uno::Exception(
503                     rtl::OUString(
504                         RTL_CONSTASCII_USTRINGPARAM(
505                             "com.sun.star.configuration.ConfigurationProvider"
506                             " factory: unknown argument ")) + name,
507                     0);
508             }
509         }
510         return static_cast< cppu::OWeakObject * >(new Service(Context, locale));
511     }
512 }
513 
514 }
515 
516 css::uno::Reference< css::uno::XInterface > createDefault(
517     css::uno::Reference< css::uno::XComponentContext > const & context)
518 {
519     return static_cast< cppu::OWeakObject * >(
520         new Service(context, rtl::OUString()));
521 }
522 
523 rtl::OUString getImplementationName() {
524     return rtl::OUString(
525         RTL_CONSTASCII_USTRINGPARAM(
526             "com.sun.star.comp.configuration.ConfigurationProvider"));
527 }
528 
529 css::uno::Sequence< rtl::OUString > getSupportedServiceNames() {
530     rtl::OUString name(
531         RTL_CONSTASCII_USTRINGPARAM(
532             "com.sun.star.configuration.ConfigurationProvider"));
533     return css::uno::Sequence< rtl::OUString >(&name, 1);
534 }
535 
536 css::uno::Reference< css::lang::XSingleComponentFactory >
537 createFactory(
538     cppu::ComponentFactoryFunc, rtl::OUString const &,
539     css::uno::Sequence< rtl::OUString > const &, rtl_ModuleCount *)
540     SAL_THROW(())
541 {
542     return new Factory;
543 }
544 
545 } }
546