Make comphelper/configuration.hxx work for localized properties

See aebf5bf223 "fdo#52232 ConfigurationSet wrapper
unusable for localized properties" for a discussion of the problems with the
original design.

1  Redesigned configmgr's localized property access to understand ['*<locale>']
paths that select the best existing value match for the requested <locale>.
Adapted ConfigurationWrapper::getLocalizedPropertyValue accordingly.

2  Redesigned ConfigurationChanges to fix the locale at instantiation time.
That takes care of ConfigurationWrapper::setLocalizedPropertyValue,
ConfigurationWrapper::getGroupReadWrite, and
ConfigurationWrapper::getSetReadWrite.  (This required an additional constructor
parameter for the ReadWriteAccess service, to specify a locale at instantiation
time.)

3  Redesigned ReadOnlyAccess to be a service that fixes the locale at
instantiation time.  That allows to take care of
ConfigurationWrapper::getGroupReadOnly and ConfigurationWrapper::getSetReadOnly.

Change-Id: I2ae7342b278b6f4222a0189a1deb2a53e204059f
This commit is contained in:
Stephan Bergmann
2012-07-25 18:54:38 +02:00
parent 3605cb216f
commit cf7c9599e7
9 changed files with 271 additions and 165 deletions

View File

@@ -148,8 +148,6 @@ public:
boost::shared_ptr< ConfigurationChanges > createChanges() const;
private:
rtl::OUString extendLocalizedPath(rtl::OUString const & path) const;
com::sun::star::uno::Reference< com::sun::star::uno::XComponentContext >
context_;

View File

@@ -64,6 +64,51 @@ struct TheConfigurationWrapper:
TheConfigurationWrapper >
{};
OUString getDefaultLocale(
css::uno::Reference< css::uno::XComponentContext > const & context)
{
css::lang::Locale locale(
css::uno::Reference< css::lang::XLocalizable >(
css::configuration::theDefaultProvider::get(context),
css::uno::UNO_QUERY_THROW)->
getLocale());
OUStringBuffer buf;
SAL_WARN_IF(
locale.Language.indexOf('-') != -1, "comphelper",
"Locale language \"" << locale.Language << "\" contains \"-\"");
buf.append(locale.Language);
SAL_WARN_IF(
locale.Country.isEmpty() && !locale.Variant.isEmpty(), "comphelper",
"Locale has empty country but non-empty variant \"" << locale.Variant
<< '"');
if (!locale.Country.isEmpty()) {
buf.append('-');
SAL_WARN_IF(
locale.Country.indexOf('-') != -1, "comphelper",
"Locale language \"" << locale.Country << "\" contains \"-\"");
buf.append(locale.Country);
if (!locale.Variant.isEmpty()) {
buf.append('-');
buf.append(locale.Variant);
}
}
return buf.makeStringAndClear();
}
OUString extendLocalizedPath(OUString const & path, OUString const & locale) {
rtl::OUStringBuffer buf(path);
buf.append("/['*");
SAL_WARN_IF(
locale.match("*"), "comphelper",
"Locale \"" << locale << "\" starts with \"-\"");
assert(locale.indexOf('&') == -1);
assert(locale.indexOf('"') == -1);
assert(locale.indexOf('\'') == -1);
buf.append(locale);
buf.append("']");
return buf.makeStringAndClear();
}
}
boost::shared_ptr< comphelper::ConfigurationChanges >
@@ -82,7 +127,9 @@ void comphelper::ConfigurationChanges::commit() const {
comphelper::ConfigurationChanges::ConfigurationChanges(
css::uno::Reference< css::uno::XComponentContext > const & context):
access_(css::configuration::ReadWriteAccess::create(context))
access_(
css::configuration::ReadWriteAccess::create(
context, getDefaultLocale(context)))
{}
void comphelper::ConfigurationChanges::setPropertyValue(
@@ -114,7 +161,8 @@ comphelper::detail::ConfigurationWrapper::get(
comphelper::detail::ConfigurationWrapper::ConfigurationWrapper(
css::uno::Reference< css::uno::XComponentContext > const & context):
context_(context), access_(css::configuration::ReadOnlyAccess::get(context))
context_(context),
access_(css::configuration::ReadOnlyAccess::create(context, "*"))
{}
comphelper::detail::ConfigurationWrapper::~ConfigurationWrapper() {}
@@ -137,7 +185,8 @@ css::uno::Any
comphelper::detail::ConfigurationWrapper::getLocalizedPropertyValue(
rtl::OUString const & path) const
{
return access_->getByHierarchicalName(extendLocalizedPath(path));
return access_->getByHierarchicalName(
extendLocalizedPath(path, getDefaultLocale(context_)));
}
void comphelper::detail::ConfigurationWrapper::setLocalizedPropertyValue(
@@ -145,7 +194,7 @@ void comphelper::detail::ConfigurationWrapper::setLocalizedPropertyValue(
rtl::OUString const & path, com::sun::star::uno::Any const & value) const
{
assert(batch.get() != 0);
batch->setPropertyValue(extendLocalizedPath(path), value);
batch->setPropertyValue(path, value);
}
css::uno::Reference< css::container::XHierarchicalNameAccess >
@@ -153,7 +202,10 @@ comphelper::detail::ConfigurationWrapper::getGroupReadOnly(
rtl::OUString const & path) const
{
return css::uno::Reference< css::container::XHierarchicalNameAccess >(
access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW);
(css::configuration::ReadOnlyAccess::create(
context_, getDefaultLocale(context_))->
getByHierarchicalName(path)),
css::uno::UNO_QUERY_THROW);
}
css::uno::Reference< css::container::XHierarchicalNameReplace >
@@ -170,7 +222,10 @@ comphelper::detail::ConfigurationWrapper::getSetReadOnly(
rtl::OUString const & path) const
{
return css::uno::Reference< css::container::XNameAccess >(
access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW);
(css::configuration::ReadOnlyAccess::create(
context_, getDefaultLocale(context_))->
getByHierarchicalName(path)),
css::uno::UNO_QUERY_THROW);
}
css::uno::Reference< css::container::XNameContainer >
@@ -188,46 +243,4 @@ comphelper::detail::ConfigurationWrapper::createChanges() const {
new ConfigurationChanges(context_));
}
rtl::OUString comphelper::detail::ConfigurationWrapper::extendLocalizedPath(
rtl::OUString const & path) const
{
rtl::OUStringBuffer buf(path);
buf.appendAscii(RTL_CONSTASCII_STRINGPARAM("/['"));
css::lang::Locale locale(
css::uno::Reference< css::lang::XLocalizable >(
css::configuration::theDefaultProvider::get(context_),
css::uno::UNO_QUERY_THROW)->
getLocale());
SAL_WARN_IF(
locale.Language.indexOf('-') != -1, "comphelper",
"Locale language \"" << locale.Language << "\" contains \"-\"");
assert(locale.Language.indexOf('&') == -1);
assert(locale.Language.indexOf('"') == -1);
assert(locale.Language.indexOf('\'') == -1);
buf.append(locale.Language);
SAL_WARN_IF(
locale.Country.isEmpty() && !locale.Variant.isEmpty(), "comphelper",
"Locale has empty country but non-empty variant \"" << locale.Variant
<< '"');
if (!locale.Country.isEmpty()) {
buf.append('-');
SAL_WARN_IF(
locale.Country.indexOf('-') != -1, "comphelper",
"Locale language \"" << locale.Country << "\" contains \"-\"");
assert(locale.Country.indexOf('&') == -1);
assert(locale.Country.indexOf('"') == -1);
assert(locale.Country.indexOf('\'') == -1);
buf.append(locale.Country);
if (!locale.Variant.isEmpty()) {
buf.append('-');
assert(locale.Variant.indexOf('&') == -1);
assert(locale.Variant.indexOf('"') == -1);
assert(locale.Variant.indexOf('\'') == -1);
buf.append(locale.Variant);
}
}
buf.appendAscii(RTL_CONSTASCII_STRINGPARAM("']"));
return buf.makeStringAndClear();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@@ -1484,6 +1484,81 @@ rtl::Reference< Node > Access::getParentNode() {
}
rtl::Reference< ChildAccess > Access::getChild(rtl::OUString const & name) {
if (getNode()->kind() == Node::KIND_LOCALIZED_PROPERTY && name.match("*")) {
OUString locale(name.copy(1));
if (locale.match("*")) {
SAL_WARN(
"configmgr",
("access best-matching localized property value via"
" \"*<locale>\" with <locale> \"")
<< locale << "\" recursively starting with \"*\"");
return getChild(locale);
}
SAL_WARN_IF(
locale.isEmpty(), "configmgr",
("access best-matching localized property value via \"*<locale>\""
" with empty <locale>; falling back to defaults"));
if (!locale.isEmpty()) {
// Find best match using an adaption of RFC 4647 lookup matching
// rules, removing "-" or "_" delimited segments from the end:
for (;;) {
rtl::Reference< ChildAccess > child(getChild(locale));
if (child.is()) {
return child;
}
sal_Int32 i = locale.getLength() - 1;
while (i > 0 && locale[i] != '-' && locale[i] != '_') {
--i;
}
if (i <= 0) {
break;
}
locale = locale.copy(0, i);
}
// As a workaround for broken xcu data that does not use shortest
// xml:lang attributes, look for the first entry with the same first
// segment as the requested language tag before falling back to
// defaults (see fdo#33638):
assert(
!locale.isEmpty() && locale.indexOf('-') == -1 &&
locale.indexOf('_') == -1);
std::vector< rtl::Reference< ChildAccess > > children(
getAllChildren());
for (std::vector< rtl::Reference< ChildAccess > >::iterator i(
children.begin());
i != children.end(); ++i)
{
OUString name2((*i)->getNameInternal());
if (name2.match(locale) &&
(name2.getLength() == locale.getLength() ||
name2[locale.getLength()] == '-' ||
name2[locale.getLength()] == '_'))
{
return *i;
}
}
}
// Defaults are the "en-US" locale, the "en" locale, the empty string
// locale, the first child (if any), or a null ChildAccess, in that
// order:
rtl::Reference< ChildAccess > child(getChild("en-US"));
if (child.is()) {
return child;
}
child = getChild("en");
if (child.is()) {
return child;
}
child = getChild(OUString());
if (child.is()) {
return child;
}
std::vector< rtl::Reference< ChildAccess > > children(getAllChildren());
if (!children.empty()) {
return children.front();
}
return rtl::Reference< ChildAccess >();
}
ModifiedChildren::iterator i(modifiedChildren_.find(name));
return i == modifiedChildren_.end()
? getUnmodifiedChild(name) : getModifiedChild(i);

View File

@@ -259,17 +259,6 @@ void ChildAccess::setProperty(
localModifications->add(getRelativePath());
}
namespace
{
rtl::OUString lcl_StripSegment(const rtl::OUString &rLocale)
{
sal_Int32 i = !rLocale.isEmpty() ? rLocale.getLength() - 1 : 0;
while (i > 0 && rLocale[i] != '-' && rLocale[i] != '_')
--i;
return rLocale.copy(0, i);
}
}
css::uno::Any ChildAccess::asValue() {
if (changedValue_.get() != 0) {
return *changedValue_;
@@ -280,66 +269,11 @@ css::uno::Any ChildAccess::asValue() {
getComponents());
case Node::KIND_LOCALIZED_PROPERTY:
{
rtl::OUString sLocale(getRootAccess()->getLocale());
if (!Components::allLocales(sLocale))
{
rtl::Reference< ChildAccess > child;
// Find best match using an adaption of RFC 4647 lookup matching
// rules, removing "-" or "_" delimited segments from the end
while (1)
{
child = getChild(sLocale);
if (child.is())
break;
rtl::OUString sTmpLocale = lcl_StripSegment(sLocale);
if (sTmpLocale.isEmpty())
break;
sLocale = sTmpLocale;
}
//Resolves: fdo#33638 Look for the first entry with the same
//first segment as the requested language tag, before falling
//back to en-US, etc.
typedef std::vector< rtl::Reference< ChildAccess > > ChildVector;
if (!child.is())
{
const ChildVector &rAllChildren = getAllChildren();
for (ChildVector::const_iterator aI = rAllChildren.begin(),
aEnd = rAllChildren.end(); aI != aEnd; ++aI)
{
rtl::OUString sLanguage = lcl_StripSegment((*aI)->getNameInternal());
if (sLocale == sLanguage)
{
child = *aI;
break;
}
}
}
// defaults are the "en-US" locale, the "en" locale, the empty
// string locale, the first child (if any), or a nil value (even
// though it may be illegal for the given property), in that
// order:
if (!child.is())
{
child = getChild(
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en-US")));
if (!child.is())
{
child = getChild(
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en")));
if (!child.is())
{
child = getChild(rtl::OUString());
if (!child.is())
{
ChildVector all(getAllChildren());
if (!all.empty())
child = all.front();
}
}
}
}
OUString locale(getRootAccess()->getLocale());
if (!Components::allLocales(locale)) {
rtl::Reference< ChildAccess > child(getChild("*" + locale));
// As a last resort, return a nil value even though it may be
// illegal for the given property:
return child.is() ? child->asValue() : css::uno::Any();
}
}

View File

@@ -30,7 +30,7 @@
<singleton name="com.sun.star.configuration.theDefaultProvider"/>
</implementation>
<implementation name="com.sun.star.comp.configuration.ReadOnlyAccess">
<singleton name="com.sun.star.configuration.ReadOnlyAccess"/>
<service name="com.sun.star.configuration.ReadOnlyAccess"/>
</implementation>
<implementation name="com.sun.star.comp.configuration.ReadWriteAccess">
<service name="com.sun.star.configuration.ReadWriteAccess"/>

View File

@@ -30,15 +30,21 @@
#include "sal/config.h"
#include "boost/noncopyable.hpp"
#include "cppuhelper/implbase2.hxx"
#include "com/sun/star/lang/XServiceInfo.hpp"
#include "com/sun/star/container/NoSuchElementException.hpp"
#include "com/sun/star/container/XHierarchicalNameAccess.hpp"
#include "com/sun/star/lang/IllegalArgumentException.hpp"
#include "com/sun/star/lang/NotInitializedException.hpp"
#include "com/sun/star/lang/XInitialization.hpp"
#include "com/sun/star/lang/XServiceInfo.hpp"
#include "com/sun/star/uno/Any.hxx"
#include "com/sun/star/uno/Exception.hpp"
#include "com/sun/star/uno/Reference.hxx"
#include "com/sun/star/uno/RuntimeException.hpp"
#include "com/sun/star/uno/Sequence.hxx"
#include "com/sun/star/uno/XComponentContext.hpp"
#include "com/sun/star/uno/XInterface.hpp"
#include "cppuhelper/implbase3.hxx"
#include "cppuhelper/weak.hxx"
#include "osl/mutex.hxx"
#include "rtl/ref.hxx"
#include "rtl/ustring.h"
@@ -57,12 +63,15 @@ namespace {
namespace css = com::sun::star;
class Service:
public cppu::WeakImplHelper2<
css::lang::XServiceInfo, css::container::XHierarchicalNameAccess >,
public cppu::WeakImplHelper3<
css::lang::XServiceInfo, css::lang::XInitialization,
css::container::XHierarchicalNameAccess >,
private boost::noncopyable
{
public:
Service(css::uno::Reference< css::uno::XComponentContext > const & context);
explicit Service(
css::uno::Reference< css::uno::XComponentContext > const & context):
context_(context) {}
private:
virtual ~Service() {}
@@ -79,30 +88,57 @@ private:
getSupportedServiceNames() throw (css::uno::RuntimeException)
{ return read_only_access::getSupportedServiceNames(); }
virtual void SAL_CALL initialize(
css::uno::Sequence< css::uno::Any > const & aArguments)
throw (css::uno::Exception, css::uno::RuntimeException);
virtual css::uno::Any SAL_CALL getByHierarchicalName(
rtl::OUString const & aName)
throw (
css::container::NoSuchElementException, css::uno::RuntimeException)
{ return root_->getByHierarchicalName(aName); }
{ return getRoot()->getByHierarchicalName(aName); }
virtual sal_Bool SAL_CALL hasByHierarchicalName(rtl::OUString const & aName)
throw (css::uno::RuntimeException)
{ return root_->hasByHierarchicalName(aName); }
{ return getRoot()->hasByHierarchicalName(aName); }
rtl::Reference< RootAccess > getRoot();
css::uno::Reference< css::uno::XComponentContext > context_;
osl::Mutex mutex_;
rtl::Reference< RootAccess > root_;
};
Service::Service(
css::uno::Reference< css::uno::XComponentContext > const & context)
void Service::initialize(css::uno::Sequence< css::uno::Any > const & aArguments)
throw (css::uno::Exception, css::uno::RuntimeException)
{
osl::MutexGuard guard(*lock());
Components & components = Components::getSingleton(context);
root_ = new RootAccess(
components, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/")),
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*")), false);
OUString locale;
if (aArguments.getLength() != 1 || !(aArguments[0] >>= locale)) {
throw css::lang::IllegalArgumentException(
"not exactly one string argument",
static_cast< cppu::OWeakObject * >(this), -1);
}
osl::MutexGuard g1(mutex_);
if (root_.is()) {
throw css::uno::RuntimeException(
"already initialized", static_cast< cppu::OWeakObject * >(this));
}
osl::MutexGuard g2(*lock());
Components & components = Components::getSingleton(context_);
root_ = new RootAccess(components, "/", locale, false);
components.addRootAccess(root_);
}
rtl::Reference< RootAccess > Service::getRoot() {
osl::MutexGuard g(mutex_);
if (!root_.is()) {
throw css::lang::NotInitializedException(
"not initialized", static_cast< cppu::OWeakObject * >(this));
}
return root_;
}
}
css::uno::Reference< css::uno::XInterface > create(

View File

@@ -30,18 +30,24 @@
#include "sal/config.h"
#include "boost/noncopyable.hpp"
#include "cppuhelper/implbase2.hxx"
#include "com/sun/star/lang/IllegalArgumentException.hpp"
#include "com/sun/star/lang/WrappedTargetException.hpp"
#include "com/sun/star/lang/XServiceInfo.hpp"
#include "com/sun/star/container/NoSuchElementException.hpp"
#include "com/sun/star/configuration/XReadWriteAccess.hpp"
#include "com/sun/star/container/NoSuchElementException.hpp"
#include "com/sun/star/lang/IllegalArgumentException.hpp"
#include "com/sun/star/lang/NotInitializedException.hpp"
#include "com/sun/star/lang/WrappedTargetException.hpp"
#include "com/sun/star/lang/XInitialization.hpp"
#include "com/sun/star/lang/XServiceInfo.hpp"
#include "com/sun/star/uno/Any.hxx"
#include "com/sun/star/uno/Exception.hpp"
#include "com/sun/star/uno/Reference.hxx"
#include "com/sun/star/uno/RuntimeException.hpp"
#include "com/sun/star/uno/Sequence.hxx"
#include "com/sun/star/uno/XComponentContext.hpp"
#include "com/sun/star/uno/XInterface.hpp"
#include "com/sun/star/util/ChangesSet.hpp"
#include "cppuhelper/implbase3.hxx"
#include "cppuhelper/weak.hxx"
#include "osl/mutex.hxx"
#include "rtl/ref.hxx"
#include "rtl/ustring.h"
#include "rtl/ustring.hxx"
@@ -59,12 +65,15 @@ namespace {
namespace css = com::sun::star;
class Service:
public cppu::WeakImplHelper2<
css::lang::XServiceInfo, css::configuration::XReadWriteAccess >,
public cppu::WeakImplHelper3<
css::lang::XServiceInfo, css::lang::XInitialization,
css::configuration::XReadWriteAccess >,
private boost::noncopyable
{
public:
Service(css::uno::Reference< css::uno::XComponentContext > const & context);
explicit Service(
css::uno::Reference< css::uno::XComponentContext > const & context):
context_(context) {}
private:
virtual ~Service() {}
@@ -81,15 +90,19 @@ private:
getSupportedServiceNames() throw (css::uno::RuntimeException)
{ return read_write_access::getSupportedServiceNames(); }
virtual void SAL_CALL initialize(
css::uno::Sequence< css::uno::Any > const & aArguments)
throw (css::uno::Exception, css::uno::RuntimeException);
virtual css::uno::Any SAL_CALL getByHierarchicalName(
rtl::OUString const & aName)
throw (
css::container::NoSuchElementException, css::uno::RuntimeException)
{ return root_->getByHierarchicalName(aName); }
{ return getRoot()->getByHierarchicalName(aName); }
virtual sal_Bool SAL_CALL hasByHierarchicalName(rtl::OUString const & aName)
throw (css::uno::RuntimeException)
{ return root_->hasByHierarchicalName(aName); }
{ return getRoot()->hasByHierarchicalName(aName); }
virtual void SAL_CALL replaceByHierarchicalName(
rtl::OUString const & aName, css::uno::Any const & aElement)
@@ -97,34 +110,57 @@ private:
css::lang::IllegalArgumentException,
css::container::NoSuchElementException,
css::lang::WrappedTargetException, css::uno::RuntimeException)
{ root_->replaceByHierarchicalName(aName, aElement); }
{ getRoot()->replaceByHierarchicalName(aName, aElement); }
virtual void SAL_CALL commitChanges()
throw (css::lang::WrappedTargetException, css::uno::RuntimeException)
{ root_->commitChanges(); }
{ getRoot()->commitChanges(); }
virtual sal_Bool SAL_CALL hasPendingChanges()
throw (css::uno::RuntimeException)
{ return root_->hasPendingChanges(); }
{ return getRoot()->hasPendingChanges(); }
virtual css::util::ChangesSet SAL_CALL getPendingChanges()
throw (css::uno::RuntimeException)
{ return root_->getPendingChanges(); }
{ return getRoot()->getPendingChanges(); }
rtl::Reference< RootAccess > getRoot();
css::uno::Reference< css::uno::XComponentContext > context_;
osl::Mutex mutex_;
rtl::Reference< RootAccess > root_;
};
Service::Service(
css::uno::Reference< css::uno::XComponentContext > const & context)
void Service::initialize(css::uno::Sequence< css::uno::Any > const & aArguments)
throw (css::uno::Exception, css::uno::RuntimeException)
{
osl::MutexGuard guard(*lock());
Components & components = Components::getSingleton(context);
root_ = new RootAccess(
components, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/")),
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*")), true);
OUString locale;
if (aArguments.getLength() != 1 || !(aArguments[0] >>= locale)) {
throw css::lang::IllegalArgumentException(
"not exactly one string argument",
static_cast< cppu::OWeakObject * >(this), -1);
}
osl::MutexGuard g1(mutex_);
if (root_.is()) {
throw css::uno::RuntimeException(
"already initialized", static_cast< cppu::OWeakObject * >(this));
}
osl::MutexGuard g2(*lock());
Components & components = Components::getSingleton(context_);
root_ = new RootAccess(components, "/", locale, true);
components.addRootAccess(root_);
}
rtl::Reference< RootAccess > Service::getRoot() {
osl::MutexGuard g(mutex_);
if (!root_.is()) {
throw css::lang::NotInitializedException(
"not initialized", static_cast< cppu::OWeakObject * >(this));
}
return root_;
}
}
css::uno::Reference< css::uno::XInterface > create(

View File

@@ -34,13 +34,20 @@
module com { module sun { module star { module configuration {
/* Provides easy read-only access to the complete configuration.
/** Provides easy read-only access to the complete configuration.
<p>This singleton is still unpublished and unstable.</p>
<p>This service is still unpublished and unstable.</p>
@since LibreOffice 3.6
@since LibreOffice 3.7
*/
singleton ReadOnlyAccess: com::sun::star::container::XHierarchicalNameAccess;
service ReadOnlyAccess: com::sun::star::container::XHierarchicalNameAccess {
/** Service constructor.
@param locale a string representation of the locale to use for localized
properties; use <code>*</code> for all-locale access
*/
create([in] string locale);
};
}; }; }; };

View File

@@ -34,13 +34,20 @@
module com { module sun { module star { module configuration {
/* Provides easy read/write access to the complete configuration.
/** Provides easy read/write access to the complete configuration.
<p>This service is still unpublished and unstable.</p>
<p>This service is still unpublished and unstable.</p>
@since LibreOffice 3.6
@since LibreOffice 3.7
*/
service ReadWriteAccess: com::sun::star::configuration::XReadWriteAccess;
service ReadWriteAccess: com::sun::star::configuration::XReadWriteAccess {
/** Service constructor.
@param locale a string representation of the locale to use for localized
properties; use <code>*</code> for all-locale access
*/
create([in] string locale);
};
}; }; }; };