[Libreoffice] Simplified C++ configuration access

Stephan Bergmann sbergman at redhat.com
Tue Jan 31 02:31:14 PST 2012

Hi all,

configmgr is a dreaded beast.  Its UNO API is notorious for re-using 
each and every generic UNO interface type that exists, in a way that 
makes it hard to see the forest for the trees.  To add insult to injury, 
people always thought it a good idea to wrap the API in C++ "helper 
classes" that are themselves baroque and obscure 
-- wtf?).

So I finally set out to simplify this (on the master branch towards LO 3.6):

At the lowest level, there are two new UNO entities, a singleton 
css.configuration.ReadOnlyAccess (of type 
css.conainer.XHierarchicalNameAccess) and a service 
css.configuration.ReadWriteAccess (of type 
css.configuration.XReadWriteAccess, subsuming 
css.container.XHierarchicalNameReplace and css.util.XChangesBatch).

These simplify access to a given configuration item (node, set, or 
property) by name.  Modification still operates in batch mode.  Each 
instance of the ReadWriteAccess service creates its own batch context, 
and modifications done through that instance's XHierarchicalNameReplace 
need to be committed via its XChangesBatch.commitChanges method.

At the next level, the new unotools/configuration.hxx header provides 
type-safe C++ wrappers for the various configuration entities on top of 
the lower level UNO services.  For example, the 
utl::ConfigurationProperty class template can be used to wrap a given 
configuration property of a specific type in a C++ class whose get and 
set member functions internally handle the conversion between the UNO 
API's generic Any and the concrete type of the given property.  There is 
also a utl::ConfigurationChanges class that wraps an instance of 
ReadWriteAccess for (batch) modification.

At the top level, new headers automatically generated in module 
officecfg provide such C++ wrappers for all of the static configuration 
structure as described by the officecfg/registry/schema/**.xcs files.

For each configuration property (with path 
/org.openoffice.Foo.Bar/Baz/Whatever) there is a C++ class 
officecfg::Foo::Bar::Baz::Whatever (declared in header 
officecfg/Foo/Bar.hxx) based on the utl::ConfigurationProperty template. 
  It is type safe (see above) and also prevents mistypings of 
configuration paths (as they are represented by qualified C++ names 
here, not string literals).

To get a property's value, all that is needed is to call the wrapper 
class's static officecfg::Foo::Bar::Baz::Whatever::get member function, 
which requires the XComponentContext as argument (use 
comphelper::getProcessComponentContext in code that does not thread the 
component context more locally).

Setting a property's value is three-step.  First obtain a 
ConfigurationChanges instance (via static member function 
utl::ConfigurationChanges::create), then call 
officecfg::Foo::Bar::Baz::Whatever::set (with the component context and 
the given ConfigurationChanges instance in addition to the new value), 
then call commit on the given ConfigurationChanges instance.

Localized configuration properties are currently treated the same as 
non-localized ones, the wrappers only give access to the value for the 
current locale.

Nillable configuration properties complicate things, in that the wrapper 
needs to use boost::optional<T> instead of plain T (e.g., 
boost::optional<rtl::OUString> vs. rtl::OUString for a string-typed 
property).  This would have been quite a nuisance as most properties 
were implicitly declared as nillable in the .xcs files (a missing 
oor:nillable attribute means oor:nillable="true"), even though in 
practice they never were used with nil values.  I therefore changed all 
those properties that already define a default <value> in 
officecfg/registry/schema/**.xcs to an explicit oor:nillable="false". 
Even though, strictly speaking, this change is incompatible, I think it 
is warranted, as it more cleanly reflects the current use of those 
properties, anyway, and should not make a difference in practice.

(One caveat though is the rare case of a non-nillable property that does 
not have a default value defined in .xcs.  While this is technically 
legal, it will cause the C++ wrapper's get function to throw a 
RuntimeException upon retrieving an empty (i.e., void) Any from 
configmgr and trying to convert it to the given type.  To avoid this, 
either specify a default value in .xcs or make sure there's always a 
value defined in .xcu.)

There are also C++ wrapper classes for all the configuration's groups 
and sets, but they are probably needed less often (and only make 
available the underlying UNO interfaces, at least for now).  One case 
where they are necessary is to add listeners to (part of) the 
configuration.  For example, to listen for changes to certain properties 
within a given configuration group, one can obtain the group's 
XMultiPropertySet via the corresponding C++ wrapper class's static get 
member function and directly add an appropriate 
XPropertiesChangeListener there.

I started to replace a number of old C++ wrappers 
(utl::SourceViewConfig, utl::SvtInetOptions, utl::SvtCacheOptions, 
util::SvtUndoOptions, SvxAsianConfig, ...), and intend to continue with 
that -- until finally things like utl::ConfigItem can be removed.  Let 
me know if you would like to help with that.


More information about the LibreOffice mailing list