Date: prev next · Thread: first prev next last
2012 Archives by date, by thread · List index


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 (utl::ConfigItem::{EnableNotification,DisableNotification,IsInValueChange} -- 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.

Stephan

Context


Privacy Policy | Impressum (Legal Info) | Copyright information: Unless otherwise specified, all text and images on this website are licensed under the Creative Commons Attribution-Share Alike 3.0 License. This does not include the source code of LibreOffice, which is licensed under the Mozilla Public License (MPLv2). "LibreOffice" and "The Document Foundation" are registered trademarks of their corresponding registered owners or are in actual use as trademarks in one or more countries. Their respective logos and icons are also subject to international copyright laws. Use thereof is explained in our trademark policy.