LibreOffice Developer's Guide: Chapter 15 - Configuration Management

    From The Document Foundation Wiki

    Capabilities

    The LibreOffice configuration management component provides a uniform interface to get and set LibreOffice configuration data in an organized manner, independent of the physical data store used for the data.

    The configuration API can be used to get and set existing configuration options. Additionally, you can extend the configuration with new settings for your own purposes. For details, see Customizing Configuration Data.

    Architecture

    LibreOffice configuration data describes the state or environment of a UNO component or the LibreOffice application. There are different kinds of configuration data:

    • Static configuration: This is data that describes the configuration of the software and hardware environment. This data is set by a setup tool and does not change at runtime. An example of static configuration data is information about installed filters.
    • Explicit settings: This is preference data that can be controlled by the user explicitly. There is a dedicated UI to change these settings. An example explicit settings are the settings controlled through the Tools ▸ Options dialogs in LibreOffice.
    • Implicit settings: This is status information that is also controlled by the user, but the user does not change explicitly. The application tracks this state in the background, making it persistent across application sessions. An example implicit settings are window positions and states, or a list of the recently used documents.

    This list is not comprehensive, but indicates the range of data characteristically stored by configuration management.

    The configuration management component organizes the configuration data in a hierarchical structure. The hierarchical structure and the names and data types of entries in this database are described by a schema. Only data that conforms to one of the installed schemas is stored in the database.

    The hierarchical database stores any hierarchical information that can be described as a configuration schema, but it is optimized to work with the data needed for application configuration and preferences. Small data items having a well-defined data type are supported efficiently, whereas large, unspecific binary objects should not be stored in the configuration database. These objects should be stored in separate files so that the configuration keeps the URLs of these files only.

    Configuration schemas are provided by the authors of applications and components that use the data. When a component is installed, the corresponding configuration schemas are installed into the configuration management system.

    Configuration data is stored in a backend data store. In LibreOffice, that data store consists of XML files in various places within the directory hierarchy.

    Configuration layers

    For a given schema, multiple layers of data may exist that are merged together at runtime. One or more of these layers define default settings, possibly shared by several users. Additionally, there is a layer specific to a single user that contains personal preferences overriding the shared settings. All changes to data affect only this user-specific layer.

    Access to the merged configuration data for a user is managed by a com.sun.star.configuration.ConfigurationProvider object.

    This provider provides views on the configuration data. A view is a subset of the entire configuration that can be navigated as an object hierarchy. The objects in this hierarchy represent nodes of the configuration hierarchy to navigate to other nodes and access values of data items.

    All configuration items have a fixed type and a name.

    The type is prescribed by the schema. The following kinds of items are available:

    • 'Properties' are data items that contain a single data value or an array of values from a limited set of basic types.
    • 'Groups' are structural nodes that contain a collection of child items of various types. The number and names of children, as well as their types, are fixed by the schema. Structural and data items can be mixed within one group.
    • 'Sets' are structural nodes that serve as dynamic containers for a variable number of elements. These elements must be all data or all structural items, and they must all be uniform. In the first case, all values have the same basic type, and in the latter case, all the sub-trees have the same structure. The schema contains templates for container elements, which are prototypes of the element structure.

    Properties hold the actual data. Group nodes form the structural skeleton defined in the schema. Set nodes are used to dynamically add and remove configuration data within the confines of the schema. Taken together, they can be used to build hierarchical structures of arbitrary complexity.

    Each configuration item has a name that uniquely identifies the item within its parent, that is, the node in the hierarchical tree that immediately contains the item under consideration. Paths spanning multiple levels of the hierarchy are built similarly to UNIX file system paths. The separator for individual name components in paths is a forward slash ('/'). Paths that begin with a slash are considered 'absolute paths' and must completely specify the location of an item within the hierarchy. Paths that start directly with a name are relative paths and describe the location of an item within one of its ancestors in the hierarchy.

    The top-level subdivisions of the configuration hierarchy are called configuration modules. Each configuration module has a schema that describes the data items available within that module. Modules are the unit of schema installation. The name of a configuration module must be globally unique. The names of configuration modules have an internal hierarchical structure using a dot ('.') as a separator, similar to Java package names. The predefined configuration modules of LibreOffice use package names from the super-package "org.openoffice.*".

    The names of container elements are specified when data items are added to a container. Data item names in the schema are limited to ASCII letters, digits and a few punctuation marks, but there are no restrictions applied to the names of container elements. This requires special handling when referring to a container element in a path. A path component addressing a container element takes the form <template-pattern>['<escaped-name>']. Here <template-pattern> can be the name of the template describing the element or an asterisk "*" to match any template. The <escaped-name> is a representation of the name of the element where a few forbidden characters are represented in an escaped form borrowed from XML. The quotes delimiting the <escaped-name> may alternatively be double quote characters "". The following character escapes are used:

    Character Escape
    & &amp;
    " &quot;
    ' &apos;

    In the table below, are some escaped forms for invented entries in the Set node /org.openoffice.Office.TypeDetection/Filters for (fictitious) filters:

    Filter Name Path Component
    Plain Text Filter['Plain Text']
    Q & A Book *["Q &amp; A Book"]
    Bob's Filter *['Bob&apos;s Filter']

    The UIName value of the last example filter would have an absolute path of /org.openoffice.Office.TypeDetection/Filters/Filter['Bob's Filter']/UIName.

    In several places in the configuration management, API arguments are passed to a newly created object instance as Sequence, for example, in the argument to createInstanceWithArguments. Such arguments are type com.sun.star.beans.NamedValue.

    Note pin.svg

    Note:
    For compatibility with older versions, arguments of type com.sun.star.beans.PropertyValue are accepted as well. Only the Name and Value fields need to be filled.

    Object Model

    The centralized entry point for configuration access is a com.sun.star.configuration.ConfigurationProvider object. This object represents a connection to a single configuration data source providing access to configuration data for a single user.

    The com.sun.star.configuration.ConfigurationProvider serves as a factory for configuration views. A configuration view provides access to the structure and data of a subset of the configuration database. This subset is accessible as a hierarchical object tree. When creating a configuration view, parameters are provided that describe the subset of the data to retrieve. In the simplest case, the only argument is an absolute configuration path that identifies a structural configuration item. This parameter is given as an argument named "nodepath". The configuration view then encompasses the sub-tree which is rooted in the indicated item.

    A configuration view is not represented by a single object, but as an object hierarchy formed by all the items that are part of the selected sub-tree. The object that comes closest to representing the view as a whole is the root element of that tree. This object is the one returned by the factory method of the com.sun.star.configuration.ConfigurationProvider. In addition to the simple node-oriented interfaces, it also supports interfaces that apply to the view as a whole.

    Configuration object model overview

    Within a configuration view, UNO objects with access interfaces are used to represent all structural items. Value items are not represented as objects, but retrieved as types, usually wrapped inside an any.

    The following types are supported for data items:

    string Plain Text (Sequence of [printable] Unicode characters)
    boolean Boolean value (true/false)
    short 16-bit integer number
    int 32-bit integer number
    long 64-bit integer number
    double Floating point number
    binary Sequence of uninterpreted octets

    Value items contain a single value, or a sequence or array of one of the basic types. The arrays must be homogeneous, that is, mixed arrays are not supported. The configuration API treats array types as atomic items, there is no built-in support for accessing or modifying individual array elements.

    Tip.svg

    Tip:
    Binary values should be used only for small chunks of data that cannot easily be stored elsewhere. For large BLOBs it is recommended to store links, for example, as URLs, in the configuration.

    For example, bitmaps for small icons might be stored in the configuration, whereas large images are stored externally.


    All of the structural objects implement the service com.sun.star.configuration.ConfigurationAccess that specifies interfaces to navigate the hierarchy and access values within the view. Different instances of this service support different sets of interfaces. The interfaces that an object supports depends on its structural type, that is, is it a group or a set, and context, that is, is it a group member, an element of a set or the root of the view.

    A configuration view can be read-only or updatable. This is determined by the access requested when creating the view, but updatability may also be restricted by access rights specified in the schema or data. The basic com.sun.star.configuration.ConfigurationAccess service specifies read-only operations. If an object is part of an updatable view and is not marked read-only in the schema or the data, it implements the extended service com.sun.star.configuration.ConfigurationUpdateAccess. This service adds interfaces to change values and modify set nodes.

    These service names are also used to create the configuration views. To create a view for read access, call createInstanceWithArguments at the com.sun.star.configuration.ConfigurationProvider to request a com.sun.star.configuration.ConfigurationAccess. To obtain an updatable view, the service com.sun.star.configuration.ConfigurationUpdateAccess must be requested.

    The object initially returned when creating a configuration view represents the root node of the view. The choice of services and interfaces it supports depends on the type of configuration item it represents. The root object has additional interfaces pertaining to the view as a whole. For example, updates of configuration data within a view are combined into batches of related changes, which exhibit transaction-like behavior. This functionality is controlled by the root object of the view.

    Configuration Data Sources

    Creating a view to configuration data is a two-step process.

    1. Connect to a data source by creating an instance of a com.sun.star.configuration.ConfigurationProvider.
    2. Ask the provider to create an access object for a specific nodepath in the configuration database using createInstanceWithArguments(). The access object can be a com.sun.star.configuration.ConfigurationAccess or a com.sun.star.configuration.ConfigurationUpdateAccess.

    Connecting to a Data Source

    The first step to access the configuration database is to connect to a configuration data source.

    To obtain a provider instance ask the global com.sun.star.lang.ServiceManager for a com.sun.star.configuration.ConfigurationProvider. Typically the first lines of code to get access to configuration data look similar to the following:

    // get my global service manager
    XMultiServiceFactory xServiceManager = UnoRuntime.queryInterface(
    XMultiServiceFactory.class, this.getRemoteServiceManager(
        "uno:socket,host=localhost,port=2083;urp;StarOffice.ServiceManager"));
    
    final String sProviderService = "com.sun.star.configuration.ConfigurationProvider";
    
    // create the provider and remember it as a XMultiServiceFactory
    XMultiServiceFactory xProvider =
        UnoRuntime.queryInterface(XMultiServiceFactory.class,
            xServiceManager.createInstance(sProviderService));

    This code creates a default com.sun.star.configuration.ConfigurationProvider. The most important interface a com.sun.star.configuration.ConfigurationProvider implements is com.sun.star.lang.XMultiServiceFactory that is used to create further configuration objects.

    The com.sun.star.configuration.ConfigurationProvider always operates in the user mode, accessing data on behalf of the current user and directing updates to the user's personal layer.

    The backend data store has several shared layers. One of these layers is used to store shared default data. The files for this layer are located in the share directory of the LibreOffice installation. Additionally, there are special layers that are used by the Extension Manager for deploying configuration data associated with extensions. For details, see Extensions.

    Arguments can be provided that determine the default behavior of views created through this com.sun.star.configuration.ConfigurationProvider. The following parameter may be used for this purpose:

    Parameter Name Type Default Comments
    Locale string The user's locale. This parameter was called "locale" in a former version. The old name is still supported for compatibility.
    Warning.svg

    Warning:
    The default configuration provider obtained when no arguments are given will always be the same object. Be careful not to call dispose() on this shared com.sun.star.configuration.ConfigurationProvider.

    If you provide any arguments, then a new instance is created. You must then call dispose() on this com.sun.star.configuration.ConfigurationProvider.


    Using a Data Source

    After a configuration provider is obtained, call createInstanceWithArguments() to create a view on the configuration data.

    The following arguments can be specified when creating a view:

    Parameter Name Type Default Comments
    nodepath string - This parameter is required. It contains an absolute path to the root node of the view.
    Locale string The user's locale

    (or "*")

    Using this parameter, specify the locale to be used for selecting locale-dependent values. Use the ISO code for a locale, for example, en-US for U.S. English.
    Note pin.svg

    Note:
    If the special value "*" is used for the locale parameter, values for all locales are retrieved. In this case, a locale-dependent property appears as a set item. The items of the set are the values for the different locales. They will have the ISO identifiers of the locales as names.

    It can be used if you want to assign values for different locales in a targeted manner.

    To create a read-only view on the data, the service com.sun.star.configuration.ConfigurationAccess is requested:

    // Create a specified read-only configuration view
    public Object createConfigurationView(String sPath) throws com.sun.star.uno.Exception {
        // get the provider to use
        XMultiServiceFactory xProvider = getProvider();
    
        // The service name: Need only read access:
        final String sReadOnlyView = "com.sun.star.configuration.ConfigurationAccess";
    
        // creation arguments: nodepath
        com.sun.star.beans.PropertyValue aPathArgument = new com.sun.star.beans.PropertyValue();
        aPathArgument.Name = "nodepath";
        aPathArgument.Value = sPath;
    
        Object[] aArguments = new Object[1];
        aArguments[0] = aPathArgument;
    
        // create the view
        Object xViewRoot = xProvider.createInstanceWithArguments(sReadOnlyView, aArguments);
    
        return xViewRoot;
    }

    To obtain updatable access, the service com.sun.star.configuration.ConfigurationUpdateAccess is requested.

    Reading Configuration Data

    ConfigurationAccess services

    The com.sun.star.configuration.ConfigurationAccess service is used to navigate through the configuration hierarchy and reading values. It also provides information about a node and its context.

    The following example shows how to collect or display information about a part of the hierarchy. For processing elements and values, our example uses its own callback Java interface IConfigurationProcessor:

    // Interface to process information when browsing the configuration tree
    public interface IConfigurationProcessor {
        // process a value item
        public abstract void processValueElement(String sPath_, Object aValue_);
        // process a structural item
        public abstract void processStructuralElement(String sPath_, XInterface xElement_);
    };

    Then, we define a recursive browser function:

    // Internal method to browse a structural element recursively in preorder
    public void browseElementRecursively(XInterface xElement, IConfigurationProcessor aProcessor)
            throws com.sun.star.uno.Exception {
        // First process this as an element (preorder traversal)
        XHierarchicalName xElementPath = UnoRuntime.queryInterface(
            XHierarchicalName.class, xElement);
    
        String sPath = xElementPath.getHierarchicalName();
    
        //call configuration processor object
        aProcessor.processStructuralElement(sPath, xElement);
    
        // now process this as a container of named elements
        XNameAccess xChildAccess =
            UnoRuntime.queryInterface(XNameAccess.class, xElement);
    
        // get a list of child elements
        String[] aElementNames = xChildAccess.getElementNames();
    
        // and process them one by one
        for (int i=0; i< aElementNames.length; ++i) {
            Object aChild = xChildAccess.getByName(aElementNames[i]);
    
            // is it a structural element (object) ...
            if ( aChild instanceof XInterface ) {
                // then get an interface
                XInterface xChildElement = (XInterface)aChild;
    
                // and continue processing child elements recursively
                browseElementRecursively(xChildElement, aProcessor);
            }
            // ... or is it a simple value
            else {
                // Build the path to it from the path of
                // the element and the name of the child
                String sChildPath;
                sChildPath = xElementPath.composeHierarchicalName(aElementNames[i]);
    
                // and process the value
                aProcessor.processValueElement(sChildPath, aChild);
            }
        }
    }

    Now a driver procedure is defined which uses our previously defined routine createConfigurationView() to create a view, and then starts processing:

    /** Method to browse the part rooted at sRootPath
        of the configuration that the Provider provides.
    
        All nodes will be processed by the IConfigurationProcessor passed.
    */
    public void browseConfiguration(String sRootPath, IConfigurationProcessor aProcessor)
            throws com.sun.star.uno.Exception {
    
        // create the root element
        XInterface xViewRoot = (XInterface)createConfigurationView(sRootPath);
    
        // now do the processing
        browseElementRecursively(xViewRoot, aProcessor);
    
        // we are done with the view - dispose it
        // This assumes that the processor
        // does not keep a reference to the elements in processStructuralElement
    
        UnoRuntime.queryInterface(XComponent.class,xViewRoot).dispose();
        xViewRoot = null;
    }

    Finally, as an example of how to put the code to use, the following is code to print the currently registered file filters:

    /** Method to browse the filter configuration.
    
        Information about installed filters will be printed.
    */
    public void printRegisteredFilters() throws com.sun.star.uno.Exception {
        final String sProviderService = "com.sun.star.configuration.ConfigurationProvider";
        final String sFilterKey = "/org.openoffice.Office.TypeDetection/Filters";
    
        // browse the configuration, dumping filter information
        browseConfiguration( sFilterKey,
            new IConfigurationProcessor () { // anonymous implementation of our custom interface
                // prints Path and Value of properties
                public void processValueElement(String sPath_, Object aValue_) {
                    System.out.println("\tValue: " + sPath_ + " = " + aValue_);
                }
                // prints the Filter entries
                public void processStructuralElement( String sPath_, XInterface xElement_) {
                    // get template information, to detect instances of the 'Filter' template
                    XTemplateInstance xInstance =
                        UnoRuntime.queryInterface( XTemplateInstance .class,xElement_);
    
                    // only select the Filter entries
                    if (xInstance != null && xInstance.getTemplateName().endsWith("Filter")) {
                        XNamed xNamed = (XNamed)UnoRuntime.queryInterface(XNamed.class,xElement_);
                        System.out.println("Filter " + xNamed.getName() + " (" + sPath_ + ")");
                    }
                }
            }
        );
    }

    For access to sub-nodes, a com.sun.star.configuration.ConfigurationAccess supports container interfaces com.sun.star.container.XNameAccess and com.sun.star.container.XChild. These interfaces access the immediate child nodes in the hierarchy , as well as com.sun.star.container.XHierarchicalNameAccess for direct access to items that are nested deeply.

    These interfaces are uniformly supported by all structural configuration items. Therefore, they are utilized by code that browses a sub-tree of the configuration in a generic manner.

    Parts of the hierarchy where the structure is known statically can also be viewed as representing a complex object composed of properties, that are composed of sub-properties themselves. This model is supported by the interface com.sun.star.beans.XPropertySet for child access and com.sun.star.beans.XHierarchicalPropertySet for access to deeply nested properties within such parts of the hierarchy. Due to the static nature of property sets, this model does not carry over to set nodes that are dynamic in nature and do not support the associated interfaces.

    For effective access to multiple properties, the corresponding com.sun.star.beans.XMultiPropertySet and com.sun.star.beans.XMultiHierarchicalPropertySet interfaces are supported.

    Note pin.svg

    Note:
    In a read-only view, all properties are marked as READONLY in com.sun.star.beans.XPropertySetInfo. Attempts to use setPropertyValue() to change the value of a property fail accordingly.

    Typically, these interfaces are used to access a known set of preferences. The following example reads grid option settings from the LibreOffice Calc configuration into this structure:

    private static class GridOptions
    {
        private boolean visible;
        private int resolution_x;
        private int resolution_y;
        private int subdivision_x;
        private int subdivision_y;
    }

    These data may be read by a procedure such as the following. It demonstrates different approaches to read data:

    // This method reads information about grid settings
    protected GridOptions readGridConfiguration()
        throws com.sun.star.uno.Exception
    {
        // The path to the root element
        final String cGridOptionsPath = "/org.openoffice.Office.Calc/Grid";
    
        // create the view
        Object xViewRoot = createConfigurationView(cGridOptionsPath);
    
        // the result structure
        GridOptions options = new GridOptions();
    
      // accessing a single nested value
        XHierarchicalPropertySet xProperties =
            UnoRuntime.queryInterface(XHierarchicalPropertySet.class, xViewRoot);
    
        Object aVisible = xProperties.getHierarchicalPropertyValue("Option/VisibleGrid");
        options.visible = ((Boolean) aVisible).booleanValue();
    
      // accessing a nested object and its subproperties
        Object xSubdivision = xProperties.getHierarchicalPropertyValue("Subdivision");
    
        XMultiPropertySet xSubdivProperties =
            UnoRuntime.queryInterface(XMultiPropertySet.class, xSubdivision);
    
        // variables for multi-element access
        String[] aElementNames = new String[] { "XAxis", "YAxis" };
    
        Object[] aElementValues = xSubdivProperties.getPropertyValues(aElementNames);
    
        options.subdivision_x = ((Integer) aElementValues[0]).intValue();
        options.subdivision_y = ((Integer) aElementValues[1]).intValue();
    
      // accessing deeply nested subproperties
        Object xResolution = xProperties.getHierarchicalPropertyValue("Resolution");
    
        XMultiHierarchicalPropertySet xResolutionProperties =
            UnoRuntime.queryInterface(XMultiHierarchicalPropertySet.class, xResolution);
    
        aElementNames[0] = "XAxis/Metric";
        aElementNames[1] = "YAxis/Metric";
    
        aElementValues = xResolutionProperties.getHierarchicalPropertyValues(aElementNames);
    
        options.resolution_x = ((Integer) aElementValues[0]).intValue();
        options.resolution_y = ((Integer) aElementValues[1]).intValue();
    
      // all options have been retrieved - clean up and return
        // we are done with the view - dispose it
    
        UnoRuntime.queryInterface(XComponent.class, xViewRoot).dispose();
    
        return options;
    }

    A com.sun.star.configuration.ConfigurationAccess also supports the interfaces com.sun.star.container.XNamed, com.sun.star.container.XHierarchicalName and com.sun.star.beans.XPropertySetInfo to retrieve information about the node, as well as interface com.sun.star.container.XChild to get the parent within the hierarchy. To monitor changes to specific items, register listeners at the interfaces com.sun.star.container.XContainer and com.sun.star.beans.XPropertySet.

    The exact set of interfaces supported depends on the role of the node in the hierarchy. For example, a set node does not support com.sun.star.beans.XPropertySet and related interfaces, but it supports com.sun.star.configuration.XTemplateContainer to get information about the template that specifies the schema of elements. The root object of a configuration view does not support com.sun.star.container.XChild, but it supports com.sun.star.util.XChangesNotifier to monitor all changes in the whole view.

    Updating Configuration Data

    A com.sun.star.configuration.ConfigurationUpdateAccess provides operations for updating configuration data, by extending the interfaces supported by a com.sun.star.configuration.ConfigurationAccess.

    ConfigurationUpdateAccess services

    For com.sun.star.beans.XPropertySet and related interfaces, the semantics are extended to set property values. Support for container interfaces is extended to set properties in group nodes, and insert or remove elements in set nodes. Thus, a com.sun.star.configuration.GroupUpdate supports interface com.sun.star.container.XNameReplace and a com.sun.star.configuration.SetUpdate supports com.sun.star.container.XNameContainer. Only complete trees having the appropriate structure are inserted for sets whose elements are complete structures as described by a template,. To support this, the set object is used as a factory that can create structures of the appropriate type. For this purpose, the set supports com.sun.star.lang.XSingleServiceFactory.

    Updates done through a configuration view are only visible within that view, providing transactional isolation. When a set of updates is ready, it must be committed explicitly to become visible beyond this view. All pending updates are then sent to the configuration provider in one batch. This batch update behavior is controlled through interface com.sun.star.util.XChangesBatch that is implemented by the root element of an updatable configuration view.

    Warning.svg

    Warning:
    When a set of changes is committed to the provider it becomes visible to other views obtained from the same provider as an atomic and consistent set of changes. Thus, in the local scope of a single com.sun.star.configuration.ConfigurationProvider a high degree of transactional behavior is achieved.

    The configuration management component does not guarantee true transactional behavior. Committing the changes to the com.sun.star.configuration.ConfigurationProvider does not ensure persistence or durability of the changes. When the provider writes back the changes to the persistent data store, they become durable. Generally, the com.sun.star.configuration.ConfigurationProvider may cache and combine requests, so that updates are propagated to the data store at a later time.

    If several sets of changes are combined before being saved, isolation and consistency may be weakened in case of failure. As long as the backend does not fully support transactions, only parts of an update request might be stored successfully, thus violating atomicity and consistency.

    If failures occur while writing configuration data into the backend data store, the com.sun.star.configuration.ConfigurationProvider resynchronizes with the data stored in the backend. The listeners are notified of any differences as if they had been stored through another view.

    In summary, there are few overall guarantees regarding transactional integrity for the configuration database, but locally, the configuration behaves as if the support is in place. The com.sun.star.configuration.ConfigurationProvider tries to provide the best approximation to transactional integrity that can be achieved considering the capabilities of the backend without compromising performance.


    The following example demonstrates how the configuration interfaces are used to feed a user-interface for preference changes. This shows the framework needed to update configuration values, and demonstrates how listeners are used with configuration views. This example concentrates on properties in group nodes with a fixed structure. It uses the same LibreOffice Calc grid settings as the previous example. It assumes that there is a class GridOptionsEditor that drives a dialog to display and edit the configuration data:

    // This method simulates editing configuration data using a GridEditor dialog class
    public void editGridOptions( )
        throws com.sun.star.uno.Exception
    {
        // The path to the root element
        final String cGridOptionsPath = "/org.openoffice.Office.Calc/Grid";
    
      // create the view
        Object xViewRoot = createUpdatableView( cGridOptionsPath );
    
        // the 'editor'
        GridOptionsEditor dialog = new GridOptionsEditor();
    
      // set up the initial values and register listeners
        // get a data access interface, to supply the view with a model
        XMultiHierarchicalPropertySet xProperties =
            UnoRuntime.queryInterface(XMultiHierarchicalPropertySet.class, xViewRoot);
    
        dialog.setModel( xProperties );
    
        // get a listener object (probably an adapter) that notifies
        // the dialog of external changes to its model
        XChangesListener xListener = dialog.createChangesListener( );
    
        XChangesNotifier xNotifier =
            UnoRuntime.queryInterface(XChangesNotifier.class, xViewRoot);
    
        xNotifier.addChangesListener( xListener );
    
        // trigger the listener
        changeSomeData( cGridOptionsPath + "/Subdivision" );
    
        if (dialog.execute() == GridOptionsEditor.SAVE_SETTINGS)
        {
            // changes have been applied to the view here
            XChangesBatch xUpdateControl =
                UnoRuntime.queryInterface(XChangesBatch.class,xViewRoot);
    
            try
            {
                xUpdateControl.commitChanges();
            }
            catch (Exception e)
            {
                dialog.informUserOfError( e );
            }
        }
    
      // all changes have been handled - clean up and return
        // listener is done now
        xNotifier.removeChangesListener( xListener );
    
        // we are done with the view - dispose it
        UnoRuntime.queryInterface(XComponent.class, xViewRoot).dispose();
    }

    In this example, the dialog controller uses the com.sun.star.beans.XMultiHierarchicalPropertySet interface to read and change configuration values. If the grid options are changed and committed in another view, changesOccurred() is sent to the listener supplied by the dialog which can then update its display accordingly.

    Besides the values for the current user, there are also default values that are determined by merging the schema with any default layers. It is possible to retrieve the default values for individual properties, and to reset a property or a set node to their default states, thus backing out any changes done for the current user. For this purpose, group nodes support the interfaces com.sun.star.beans.XPropertyState and com.sun.star.beans.XMultiPropertyStates, offering operations to query if a property assumes its default state or the default value, and to reset an updatable property to its default state. The com.sun.star.beans.Property structs available through getPropertyByName() or getProperties() are used to determine if a particular item or node supports this operation.

    Individual set elements can not be reset because set nodes do not support com.sun.star.beans.XPropertyState. Instead a com.sun.star.configuration.SetAccess supports com.sun.star.beans.XPropertyWithStatethat resets the set as a whole.

    The following is an example code using this feature to reset the LibreOffice Calc grid settings used in the preceding examples to their default state:

    /// This method resets the grid settings to their default values
    protected void resetGridConfiguration()
        throws com.sun.star.uno.Exception
    {
        // The path to the root element
        final String cGridOptionsPath = "/org.openoffice.Office.Calc/Grid";
    
        // create the view
        Object xViewRoot = createUpdatableView(cGridOptionsPath);
    
      // resetting a single nested value
        XHierarchicalNameAccess xHierarchicalAccess =
            UnoRuntime.queryInterface(XHierarchicalNameAccess.class, xViewRoot);
    
        // get using absolute name
        Object xOptions = xHierarchicalAccess.getByHierarchicalName(cGridOptionsPath + "/Option");
    
        XPropertyState xOptionState =
            UnoRuntime.queryInterface(XPropertyState.class, xOptions);
    
        xOptionState.setPropertyToDefault("VisibleGrid");
    
      // resetting more deeply nested values
        Object xResolutionX = xHierarchicalAccess.getByHierarchicalName("Resolution/XAxis");
        Object xResolutionY = xHierarchicalAccess.getByHierarchicalName("Resolution/YAxis");
    
        XPropertyState xResolutionStateX =
            UnoRuntime.queryInterface(XPropertyState.class, xResolutionX);
        XPropertyState xResolutionStateY =
            UnoRuntime.queryInterface(XPropertyState.class, xResolutionY);
    
        xResolutionStateX.setPropertyToDefault("Metric");
        xResolutionStateY.setPropertyToDefault("Metric");
    
      // resetting multiple sibling values
        Object xSubdivision = xHierarchicalAccess.getByHierarchicalName("Subdivision");
    
        XMultiPropertyStates xSubdivisionStates =
            UnoRuntime.queryInterface(XMultiPropertyStates.class, xSubdivision);
    
        xSubdivisionStates.setAllPropertiesToDefault();
    
        // commit the changes
        XChangesBatch xUpdateControl =
            UnoRuntime.queryInterface(XChangesBatch.class,xViewRoot);
    
        xUpdateControl.commitChanges();
    
        // we are done with the view - dispose it
        UnoRuntime.queryInterface(XComponent.class, xViewRoot).dispose();
    }

    {{Note|1=Currently, group nodes do not support the attribute MAYBEDEFAULT set in the com.sun.star.beans.Property structure available from com.sun.star.beans.XPropertySetInfo. Attempts to use setPropertyToDefault to reset an entire group node fail.

    Also, because the group nodes can not be reset, the setPropertyToDefault or setAllPropertiesToDefault cannot be used to reset all descendants of a node.

    It is intended to lift this restriction in a future release. To avoid unexpected changes in behavior when this change is introduced, you should apply setPropertyToDefault</idlm> only to actual properties, such as value items, or set nodes. In particular, you should avoid [https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1beans_1_1XMultiPropertyStates.html#:~:text=◆-,setAllPropertiesToDefault%28%29 setAllPropertiesToDefault() on group nodes.]

    A more comprehensive example is provided that shows how set elements are created and added, and how it employs advanced techniques for reducing the amount of data that needs to be loaded.

    This example uses the LibreOffice configuration module org.openoffice.Office.DataAccess. This component has a set item DataSources that contains group items described by the template DataSourceDescription. A data source description holds information about the settings required to connect to a data source.

    The template org.openoffice.Office.DataAccess/DataSourceDescription has the following properties that describe the data source connection:

    Name Type Comment
    URL String Data source URL.
    IsPasswordRequired Boolean Is a password needed to connect.
    TableFilter String [] Filters tables for display.
    TableTypeFilter String [] Filters tables for display.
    User String User name to be used for connecting.
    LoginTimeout int Default time-out for connection attempt.
    SuppressVersionColumns Boolean Controls display of certain data.
    DataSourceSettings set node Contains DataSourceSetting entries that contain driver-specific settings.
    Bookmarks set node Contains Bookmark entries that link to related documents, for example, Forms.

    It also contains the binary properties NumberFormatSettings and LayoutInformation that store information for layout and display of the data source contents. It also contains the set items Tables and Queries containing the layout information for the data access views.

    The example shows a procedure that creates and saves basic settings for connecting to a new data source. It uses an asynchronous com.sun.star.configuration.ConfigurationUpdateAccess. Thus, when commitChanges is called, the data becomes visible at the com.sun.star.configuration.ConfigurationProvider, but is only stored in the provider's cache. It is written to the data store at later when the cache is automatically flushed by the com.sun.star.configuration.ConfigurationProvider. As this is done in the background there is no exception when the write-back fails.

    Warning.svg

    Warning:
    The recommended method to configure a new data source is to use the com.sun.star.sdb.DatabaseContext service as described in DatabaseContext. This is a high-level service that ensures that all the settings required to establish a connection are properly set.


    Among the parameters of the routine is the name of the data source that must be chosen to uniquely identify the data source from other parameters directly related to the above properties. There also is a parameter to pass a list of entries for the DataSourceSettings set.

    The resulting routine is:

    // This method stores a data source for given connection data
    void storeDataSource(
        String sDataSourceName,
        String sDataSourceURL,
        String sUser,
        boolean bNeedsPassword,
        int nTimeout,
        com.sun.star.beans.NamedValue [] aDriverSettings,
        String [] aTableFilter
    )
        throws com.sun.star.uno.Exception
    {
      // create the view and get the data source element
        Object xDataSource = createDataSourceDescription(getProvider(),sDataSourceName);
    
      // set the values
        XPropertySet xDataSourceProperties =
            UnoRuntime.queryInterface(XPropertySet.class, xDataSource);
    
        xDataSourceProperties.setPropertyValue("URL",  sDataSourceURL  );
        xDataSourceProperties.setPropertyValue("User", sUser  );
        xDataSourceProperties.setPropertyValue("IsPasswordRequired", Boolean.valueOf( bNeedsPassword ) );
        xDataSourceProperties.setPropertyValue("LoginTimeout", Integer.valueOf( nTimeout ) );
    
        if ( aTableFilter != null )
            xDataSourceProperties.setPropertyValue("TableFilter",  aTableFilter  );
    
      // store the driver-specific settings
        if (aDriverSettings != null)
        {
            Object xSettingsSet = xDataSourceProperties.getPropertyValue("DataSourceSettings");
            storeSettings( xSettingsSet, aDriverSettings);
        }
    
      // save the data and dispose the view
        // recover the view root
        Object xViewRoot = getViewRoot(xDataSource);
    
        // commit the changes
        XChangesBatch xUpdateControl =
            UnoRuntime.queryInterface(XChangesBatch.class,xViewRoot);
    
        xUpdateControl.commitChanges();
    
        // now clean up
        UnoRuntime.queryInterface(XComponent.class, xViewRoot).dispose();
    }

    Notice the function createDataSourceDescription in our example. It is called to get a DataSourceDescription instance to access a pre-existing item, or create and insert a new item using the passed name.

    /** This method gets the DataSourceDescription for a data source.
        It either gets the existing entry or creates a new instance.
    */
    Object createDataSourceDescription(XMultiServiceFactory xProvider, String sDataSourceName )
        throws com.sun.star.uno.Exception
    {
        // The service name: Need an update access:
        final String cUpdatableView = "com.sun.star.configuration.ConfigurationUpdateAccess";
    
          // The path to the DataSources set node
        final String cDataSourcesPath = "/org.openoffice.Office.DataAccess/DataSources";
    
        // creation arguments: nodepath
        com.sun.star.beans.PropertyValue aPathArgument = new com.sun.star.beans.PropertyValue();
        aPathArgument.Name = "nodepath";
        aPathArgument.Value = cDataSourcesPath ;
    
        Object[] aArguments = new Object[1];
        aArguments[0] = aPathArgument;
    
        // create the view
        Object xViewRoot =
            xProvider.createInstanceWithArguments(cUpdatableView, aArguments);
    
        XNameAccess xSetOfDataSources =
            UnoRuntime.queryInterface(XNameAccess.class,xViewRoot);
    
        Object xDataSourceDescriptor = null; // the result
        if ( xSetOfDataSources .hasByName( sDataSourceName ))
        {
            // the element is there
            try
            {
                // the view should point to the element directly, so we need to extend the path
                XHierarchicalName xComposePath = UnoRuntime.queryInterface(XHierarchicalName.class, xSetOfDataSources );
    
                String sElementPath = xComposePath.composeHierarchicalName( sDataSourceName );
    
                // use the name of the element now
                aPathArgument.Value = sElementPath;
    
                // create another view now
                Object[] aDeepArguments = new Object[1];
                aDeepArguments[0] = aPathArgument;
    
                // create the view
                xDataSourceDescriptor  =
                      xProvider.createInstanceWithArguments(cUpdatableView, aDeepArguments);
    
                if ( xDataSourceDescriptor != null) // all went fine
                {
                    // dispose the other view
                    UnoRuntime.queryInterface(XComponent.class, xViewRoot).dispose();
                    xViewRoot = null;
                }
            }
            catch (Exception e)
            {
              // something went wrong, we retry with a new element
                System.err.println("WARNING: An exception occurred while creating a view for an existing data source: " + e);
                xDataSourceDescriptor  = null;
            }
        }
    
        // do we have a result element yet ?
        if ( xDataSourceDescriptor == null)
        {
            // get the container
            XNameContainer xSetUpdate =
                UnoRuntime.queryInterface(XNameContainer.class, xViewRoot);
    
            // create a new detached set element (instance of DataSourceDescription)
            XSingleServiceFactory xElementFactory =
                UnoRuntime.queryInterface(XSingleServiceFactory.class, xSetUpdate);
    
            // the new element is the result !
              xDataSourceDescriptor  = xElementFactory.createInstance();
    
            // insert it - this also names the element
            xSetUpdate.insertByName( sDataSourceName ,  xDataSourceDescriptor  );
        }
    
        return xDataSourceDescriptor ;
    }

    A method is required to recover the view root from an element object, because it is unknown if the item is the root of the view or a descendant :

    // This method get the view root node given an interface to any node in the view
    public static Object getViewRoot(Object xElement)
    {
        Object xResult = xElement;
    
        // set the result to its parent until that would be null
        Object xParent;
        do
        {
            XChild xParentAccess =
                UnoRuntime.queryInterface(XChild.class,xResult);
    
            if (xParentAccess != null)
                xParent = xParentAccess.getParent();
            else
                xParent = null;
    
            if (xParent != null)
                xResult = xParent;
          }
          while (xParent != null);
    
          return xResult;
    }

    Another function used is storeDataSource is storeSettings to store an array of com.sun.star.beans.NamedValues in a set of DataSourceSetting items. A DataSourceSetting contains a single property named Value that is set to any of the basic types supported for configuration values. This example demonstrates the two steps required to add a new item to a set node:

    /// this method stores a number of settings in a set node containing DataSourceSetting objects
    void storeSettings(Object xSettingsSet, com.sun.star.beans.NamedValue [] aSettings )
        throws com.sun.star.uno.Exception
    {
        if (aSettings == null)
            return;
    
        // get the settings set as a container
        XNameContainer xSettingsContainer =
            UnoRuntime.queryInterface( XNameContainer.class, xSettingsSet);
    
        // and get a factory interface for creating the entries
        XSingleServiceFactory xSettingsFactory =
            UnoRuntime.queryInterface(XSingleServiceFactory.class, xSettingsSet);
    
        // now insert the individual settings
        for (int i = 0; i < aSettings.length; ++i) {
            // create a DataSourceSetting object
            XPropertySet xSetting = UnoRuntime.queryInterface( XPropertySet.class, xSettingsFactory.createInstance() );
    
            // can set the value before inserting
            xSetting.setPropertyValue( "Value", aSettings[i].Value );
    
            // and now insert or replace as appropriate
            if (xSettingsContainer.hasByName( aSettings[i].Name ))
                xSettingsContainer.replaceByName( aSettings[i].Name, xSetting );
            else
                xSettingsContainer.insertByName( aSettings[i].Name, xSetting );
        }
    }

    Besides adding a freshly created instance of a template, a set item can be removed from a set and added to any other set supporting the same template for its elements, provided both sets are part of the same view. You cannot move a set item between views, as this contradicts the transactional isolation of views. The set item you removed in one view will still be in its old place in the other. If a set item is moved between sets in one view and the changes are committed, the change appears in another overlapping view as removal of the original item and insertion of a new element in the target location, not as relocation of an identical element.

    Tip.svg

    Tip:
    The methods setName() and setParent() are supported by a com.sun.star.configuration.ConfigurationUpdateAccess only if it is a com.sun.star.configuration.SetElement. They offer another way to move an item within a set or from one set to another set.

    In the current release of LibreOffice, these methods are not supported correctly. You can achieve the same effect by using a sequence of remove item - insert item.

    In some cases you need to commit the changes in the current view between these two steps.

    To rename an item:

    /// Does the same as xNamedItem.setName(sNewName) should do
    void renameSetItem(XNamed xNamedItem, String sNewName)
        throws com.sun.star.uno.Exception
    {
        XChild xChildItem = UnoRuntime.queryInterface(XChild.class, xNamedItem);
    
        XNameContainer xParentSet = UnoRuntime.queryInterface( XNameContainer.class, xChildItem.getParent() );
    
        String sOldName = xNamedItem.getName();
    
        // now rename the item
        xParentSet.removeByName(sOldName);
        xParentSet.insertByName(sNewName,xNamedItem);
    }

    To move an item to a different parent:

    /// Does the same as xChildItem.setParent( xNewParent ) should do
    void moveSetItem(XChild xChildItem, XNameContainer xNewParent)
        throws com.sun.star.uno.Exception
    {
        XNamed xNamedItem = UnoRuntime.queryInterface(XNamed.class, xChildItem);
    
        XNameContainer xOldParent = UnoRuntime.queryInterface( XNameContainer.class, xChildItem.getParent() );
    
        String sItemName = xNamedItem.getName();
    
        // now rename the item
        xOldParent.removeByName(sItemName);
        xNewParent.insertByName(sItemName,xChildItem);
    }


    Customizing Configuration Data

    The configuration management API is a data manipulation API. There is no support for data definition functionality. You cannot programmatically inspect, modify or create a configuration schema.

    You can add configuration data for your own components by creating a new configuration schema file and installing it into the configuration backend. You can also create a configuration data file for either your own schema or an existing schema and import it into the configuration database.

    The backend uses these XML formats internally as well. Some information about the internal organization of the backend is available at https://people.freedesktop.org/~vmiklos/2013/oor-document-format.html.

    Creating a Custom Configuration Schema

    A configuration schema file is an XML file that conforms to the OOR Registry Component Schema Format defined in https://wiki.openoffice.org/wiki/OOR-document-format. Normally, configuration schema files carry the extension .xcs.

    Warning.svg

    Warning:
    Not all schemas that can be described using the OOR Registry Component Schema Format are accepted by the current version of LibreOffice. In particular support for extensible nodes is limited: Only group nodes that otherwise contain no child elements may be marked as extensible. Such nodes are represented as set nodes having property elements in the API.


    As an example, consider the schema of the rg.openoffice.Office.Addons component. For details about configuration for Addon components, see Configuration.

    <?xml version='1.0' encoding='UTF-8'?>
    <oor:component-schema oor:name="Addons" oor:package="org.openoffice.Office" xml:lang="en-US" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <templates>
            <group oor:name="MenuItem">
                <prop oor:name="URL" oor:type="xs:string"/>
                <prop oor:name="Title" oor:type="xs:string" oor:localized="true"/>
                <prop oor:name="ImageIdentifier" oor:type="xs:string"/>
                <prop oor:name="Target" oor:type="xs:string"/>
                <prop oor:name="Context" oor:type="xs:string"/>
                <set oor:name="Submenu" oor:node-type="MenuItem"/>
            </group>
            <group oor:name="PopupMenu">
                <prop oor:name="Title" oor:type="xs:string" oor:localized="true"/>
                <prop oor:name="Context" oor:type="xs:string"/>
                <set oor:name="Submenu" oor:node-type="MenuItem"/>
            </group>
            <group oor:name="ToolBarItem">
                <prop oor:name="URL" oor:type="xs:string"/>
                <prop oor:name="Title" oor:type="xs:string" oor:localized="true"/>
                <prop oor:name="ImageIdentifier" oor:type="xs:string"/>
                <prop oor:name="Target" oor:type="xs:string"/>
                <prop oor:name="Context" oor:type="xs:string"/>
            </group>
            <group oor:name="UserDefinedImages">
                <prop oor:name="ImageSmall" oor:type="xs:hexBinary"/>
                <prop oor:name="ImageBig" oor:type="xs:hexBinary"/>
                <prop oor:name="ImageSmallHC" oor:type="xs:hexBinary"/>
                <prop oor:name="ImageBigHC" oor:type="xs:hexBinary"/>
                <prop oor:name="ImageSmallURL" oor:type="xs:string"/>
                <prop oor:name="ImageBigURL" oor:type="xs:string"/>
                <prop oor:name="ImageSmallHCURL" oor:type="xs:string"/>
                <prop oor:name="ImageBigHCURL" oor:type="xs:string"/>
            </group>
            <group oor:name="Images">
                <prop oor:name="URL" oor:type="xs:string"/>
                <node-ref oor:name="UserDefinedImages" oor:node-type="UserDefinedImages"/>
            </group>
            <set oor:name="ToolBarItems" oor:node-type="ToolBarItem"/>
        </templates>
        <component>
            <group oor:name="AddonUI">
                <set oor:name="AddonMenu" oor:node-type="MenuItem"/>
                <set oor:name="Images" oor:node-type="Images"/>
                <set oor:name="OfficeMenuBar" oor:node-type="PopupMenu"/>
                <set oor:name="OfficeToolBar" oor:node-type="ToolBarItems"/>
                <set oor:name="OfficeHelp" oor:node-type="MenuItem"/>
            </group>
        </component>
    </oor:component-schema>

    The schema has an XML root node that contains two parts, a list of template definitions and a definition of the component tree. The root node also declares XML namespaces that are used within the schema. Template definitions describe configuration tree fragments, which can be reused within the schema by reference or as element type of set nodes. In the case of set elements they serve as blueprints from which new instances of set items are built by the configuration management API components. Templates can either be group nodes or set nodes. The component part describes the actual data tree of the component. The component node is a special group node that represents the root of the component tree. Both parts are optional in the schema definition. A schema may provide only templates for reuse by other components, or it may describe only a component tree without defining any templates of its own.

    The tree structure is built from group nodes, set nodes. Properties are represented as prop nodes. The XML elements contain the information necessary to identify the node and its type as attributes. They may further contain extra child elements that contain human-readable descriptions of the node or that specify constraints on the permissible or meaningful values of properties. Property elements may also contain a default value.

    A schema must be installed into the backend to be usable. Once a schema is installed the component it describes can be accessed through the configuration management API. An installed schema is assumed to not change anymore.

    Known Issues regarding schema handling

    Preparing Custom Configuration Data

    A configuration data file is an XML file that conforms to the OOR Registry Update Format defined in https://wiki.openoffice.org/wiki/OOR-document-format. Normally, configuration data files carry the extension .xcu.

    A configuration data file contains changes to a configuration tree. When configuration data is read, an initial configuration tree is constructed from the component data described in the component schema. Then the configuration data files from all applicable layers are successively applied to this configuration tree. A layer is applied by applying the changes to the tree described by the data file while respecting any access control attributes and ensuring that the changes conform to the schema. Schema violations, like updates that specify a data type that disagrees with the type specified in the schema, are considered errors and result in complete failure to read the component.

    As an example, consider data for a sample Addon component. For details about configuration for Addon components, see Configuration.

    <?xml version='1.0' encoding='UTF-8'?>
    <oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="Addons" oor:package="org.openoffice.Office">
        <node oor:name="AddonUI">
            <node oor:name="OfficeMenuBar">
                <node oor:name="org.openoffice.example.addon" oor:op="replace">
                    <prop oor:name="Title" oor:type="xs:string">
                        <value xml:lang="en-US">Add-On example</value>
                        <value xml:lang=”de”>Add-On Beispiel</value>
                    </prop>
                    <prop oor:name="Context" oor:type="xs:string">
                        <value>com.sun.star.text.TextDocument</value>
                    </prop>
                    <node oor:name="Submenu">
                        <node oor:name="m1" oor:op="replace">
                            <prop oor:name="URL">
                                <value>org.openoffice.Office.addon.example:Function1</value>
                            </prop>
                            <prop oor:name="Title">
                                <value xml:lang=”en-US”>Add-On Function 1</value>
                                <value xml:lang="de">Add-On Funktion 1</value>
                            </prop>
                            <prop oor:name="Target">
                                <value>_self</value>
                            </prop>
                        </node>
                    </node>
                </node>
            </node>
        </node>
    </oor:component-data>

    The component-data root element of the configuration data XML corresponds to the component element of the associated schema. The elements of the update format do not reflect the distinction between set nodes and group nodes. All changes to structural nodes are expressed in the same way as simple node elements. Changes to property nodes use their own element tag. If a property has been declared as localized in the schema, the data file may contain different values for different locales. Changes may contain an operation attribute, which describes how the data is to be combined with preexisting data from the configuration tree, in order to obtain the result configuration data tree.

    Changes also may contain access control attributes that restrict how the data can be overwritten by data in subsequent data layers. These access control attributes are not currently available directly through the configuration management API. But if a node in a default layer is protected from being overwritten by the user layer, the protection is reflected in the API by marking the corresponding node as read-only or non-removable.

    Configuration data must be imported or installed into the backend to be effective.

    Installing Custom Configuration Data

    The only supported way to install configuration schema or data files is by using the Extension Manager to deploy configuration data as part of an extension. For details, see Extensions.

    Heckert GNU white.svg

    Content on this page is licensed under the Public Documentation License (PDL).