Skip navigation links
org.netbeans.modules.settings/1 1.60

Package org.netbeans.spi.settings

Makes it possible to store settings in a custom human-readable storage format or reuse any existing format by using convertors.

See: Description

Package org.netbeans.spi.settings Description

Makes it possible to store settings in a custom human-readable storage format or reuse any existing format by using convertors.

Contents

Settings API

What are Convertors?

Convertors are intended, as the name indicates, to convert objects from and to its persistent state. They are supposed to facilitate module writers to persist objects in a human readable format and to allow to separate code related information like class names from data to prevent to later compatibility issues.

Make Own Convertor

Subclass Convertor

Creating a convertor means creating a new class which subclasses the abstract Convertor class.

Methods read and write should contain an converting algorithm.

Methods registerSaver and unregisterSaver allow to define own logic to detect changes in a setting object and notify the framework about these changes via Saver interface. Notifications are interpreted as requests to store the object or to mark it as changed (e.g the framework provides SaveCookie).

E.g. you can incorporate the property change support in your setting object as the source of notifications. The Convertor.registerSaver will register own listener and filter fired events you want the framework to be notified.

Register Convertor

If you have already written own convertor class it is necessary to register it.

Each .settings file containing values in the format supported by your convertor has to be headed by DOCTYPE containing a public identifier defining grammar used for entity registrations.

First you should register an entity beneath xml/entities according to public identifier in the module layer. That registration has to be in the shape recognizable by a system entity resolver.

<folder name="xml">
  <folder name="entities">
    <!--Entity registration-->
    <folder name="Vendor_org_netbeans_modules_foo">
      <file name="DTD_FooSetting_1_0" url="nbres:/org/netbeans/modules/foo/entity-1_0.dtd">
        <attr name="hint.originalPublicID"
          stringvalue="-//Vendor org.netbeans.modules.foo//DTD FooSetting 1.0//EN"/>
      </file>
    </folder>
  </folder>
</folder>

Next register the Environment Provider associated with the entity under xml/lookups. The registration has to contain following attributes

<folder name="xml">
  <folder name="lookups">
    <folder name="Vendor_org_netbeans_modules_foo">
      <file name="DTD_FooSetting_1_0.instance">
        <!--Environment Provider provided by the settings module-->
        <attr name="instanceCreate" methodvalue="org.netbeans.api.settings.Factory.create"/>
        <!--Custom convertor object-->
        <attr name="settings.convertor" methodvalue="org.netbeans.modules.foo.FooConvertor.create"/>
        <!--class name of the setting object-->
        <attr name="settings.instanceClass" stringvalue="org.netbeans.modules.foo.FooSetting"/>
        <!--the setting object; optional attribute; here you can specify
          a factory producing object into which stored data are read. The produced object
          should conform the settings.instanceClass attribute-->
        <attr name="settings.instanceCreate" methodvalue="org.netbeans.modules.foo.FooSetting.create"/>
        <!--class name and subclass names of the setting object separated by comma;
          used for performance reasons; be careful what you include or exclude here to ensure
          your setting is accessible via the Lookup API -->
        <attr name="settings.instanceOf" stringvalue="org.netbeans.modules.foo.FooSetting"/>
        <!--plus attributes specific to your convertor-->
      </file>
    </folder>
  </folder>
</folder>

Runtime Instances

Now if you need to create new persistent instances of your setting object in the runtime you have to register its class with attribute settings.providerPath under xml/memory folder. The attribute has to contain path to the environment provider associated with a proper entity registration.
<folder name="xml">
  <folder name="memory">
    <folder name="org">
      <folder name="netbeans">
        <folder name="modules">
          <folder name="foo">
            <!--allows to create .settings file from memory via InstanceDataObject.create-->
            <file name="FooSetting">
              <attr name="settings.providerPath"
                 stringvalue="xml/lookups/Vendor_org_netbeans_modules_foo/DTD_FooSetting_1_0.instance"/>
            </file>
          </folder>
        </folder>
      </folder>
    </folder>
  </folder>
</folder>
To create a persistent instance use method org.openide.loaders.InstanceDataObject.create. The framework will look up the provider registration for the exact class of the object passed into the method.

If you have a special setting convertor capable of converting of objects of its class and of all of its superclasses you might want to specify also attribute settings.subclasses with boolean value set to true like this:

<folder name="xml">
  <folder name="memory">
    <folder name="org">
      <folder name="netbeans">
        <folder name="modules">
          <folder name="foo">
            <!--allows to create .settings file from memory via InstanceDataObject.create-->
            <file name="FooSetting">
              <attr name="settings.providerPath"
                 stringvalue="xml/lookups/Vendor_org_netbeans_modules_foo/DTD_FooSetting_1_0.instance"/>
              <attr name="settings.subclasses"
                 boolvalue="true"/>
            </file>
          </folder>
        </folder>
      </folder>
    </folder>
  </folder>
</folder>

Please use the attribute settings.subclasses only in case you are sure your convertor can handle all subclasses!

Composed Content

In order to allow to reuse existing convertors and to put their output together into one XML document the settings module introduces a DOMConvertor allowing to delegate reading and writing of setting object's properties (e.g. complex objects) to already existing convertors.

The delegating is handled by methods DOMConvertor.delegateRead/delegateWrite which are able to look up a proper registered Convertor according to a class of a setting object (see registration for runtime instances) and according to a public ID (see registration for entities) specified as an attribute (dtd_public_id) of a xml element.

There are two ways in which is the delegation handled:

  1. in case a DOMConvertor is available read/write operation is delegated to DOMConvertor.readElement or DOMConvertor.writeElement which are supposed to be rewritten by subclasses.
  2. in case a DOMConvertor is not available a plain Convertor is used and its output is encapsulated in a CDATA block inside the domconvertor element.
Follows XML fragment showing some complex property described by tag complex_property which was written via SubclassedDOMConvertor.writeElement and plugged by DOMConvertor.delegateWrite into document:
  ...
    <complex_property dtd_public_id="-//Vendor ...//EN">
      <foo />
    </complex_property>
  ...
Other XML fragment shows a complex property written via a SubclassedConvertor.write and plugged by DOMConvertor.delegateWrite into document:
  ...
    <domconvertor dtd_public_id="-//Vendor ...//EN"><[!CDATA[...]]></domconvertor>
  ...

The DOMConvertor.delegateRead/delegateWrite also solve multiple references of an object that can appear in scope of a XML document. Attributes ID and IDREF are used in accordance with the XML specification .

  ...
    <foosetting dtd_public_id="-//Vendor ...//EN" id="1">
    </foosetting
  ...
    <domconvertor idref="1"/>
  ...

Utilizing of Document Object Model (DOM) Level 2 Core Specification facilitates code formatting of the output and also provides APIs describing the hierarchy of nested elements, arbitrary complex.

Finding Settings

To find out your setting you can use the lookup system. In such case you have to place your .settings file under Services folder. This is useful for settings shared among modules.

Otherwise you can place the file under own folder and use DataSystem API to get the object.

FileObject fo = FileUtil.getConfigFile("YourFolder/YourSetting.settings");
DataObject dobj = DataObject.find(fo);
InstanceCookie ic = (InstanceCookie) dobj.getCookie(InstanceCookie.class);
Object setting = ic.instanceCreate();

Versioning Settings

Replacing the setting object class

Update the environment provider registration mainly file attributes settings.instanceClass and settings.instanceOf. If an old class is referenced inside the format use META-INF/netbeans/translate.names file.

Replacing the setting format

Register an entity for the newly introduced format. Register your setting class with proper file attribute settings.providerPath as was described in the section about creating settings in the runtime. Do not remove the old registration! The framework will read the file via original convertor but changes will be stored via new one.

Properties Format

For some (and maybe many) modules it is sufficient to store its values as name, value string pairs. For them the Settings module provides a special convertor that handles content of java.util.Properties. The format definition is as follows:
<!ELEMENT properties (property*)>
<!ELEMENT property EMPTY>
<!ATTLIST property
    name    CDATA   #REQUIRED
    value   CDATA   #REQUIRED
>

To create a setting object class compatible with the XMLProperties convertor the class has to contain the property change support (like java.beans.ProperyChangeSupport) to make possible to register java.beans.PropertyChangeListener, implement public default constructor (if the settings.instanceCreate attribute is not used). To collect data supposed to be persisted following methods have to be present:

<ANY-ACCESS-MODIFIER> void readProperties(java.util.Properties p)
and
<ANY-ACCESS-MODIFIER> void writeProperties(java.util.Properties p)
It is the setting object concern to collect all properties of its super classes. Since version 1.18, the readProperties can also return an Object. In such case, the XMLPropertiesConvertor replaces the current object with the returned one.

The XMLProperties convertor also makes possible to prevent automatic storing of the setting object by using file attribute xmlproperties.preventStoring in the module layer. The attribute can contain the value whether the setting object will be stored automatically (xmlproperties.preventStoring==false) or SaveCookie will be provided. Default value is false. Usage

<attr name="xmlproperties.preventStoring" boolvalue="[true|false]"/>
The second additional attribute is xmlproperties.ignoreChanges specifying property change events by comma separated list of property names which will be ignored. You can use special token all to ignore all events. Usage
<attr name="xmlproperties.ignoreChanges" stringvalue="name[, ...]"/>

Here is an example of registrations in a module layer necessary to properly handle FooSetting object persisted in the properties format.

<folder name="xml">

  <!--First you need to register own public identifier-->
  <folder name="entities">
    <!--the public identifier registration-->
    <folder name="NetBeans_org_netbeans_modules_foo">
      <file name="DTD_FooSetting_1_0"
            url="nbres:/org/netbeans/modules/settings/resources/properties-1_0.dtd">
        <attr name="hint.originalPublicID"
              stringvalue="-//NetBeans org.netbeans.modules.foo//DTD FooSetting 1.0//EN"/>
      </file>
    </folder>
  </folder>
  
  <!--Follows XMLPropertiesConvertor registration with proper attributes-->
  <folder name="lookups">
    <!--the environment provider registration-->
    <folder name="NetBeans_org_netbeans_modules_foo">
      <file name="DTD_FooSetting_1_0.instance">
        <!--env. provider-->
        <attr name="instanceCreate" methodvalue="org.netbeans.api.settings.Factory.create"/>
        <!--XMLProperties convertor provided by the settings module-->
        <attr name="settings.convertor" methodvalue="org.netbeans.api.settings.Factory.properties"/>
        <attr name="settings.instanceClass" stringvalue="org.netbeans.modules.foo.FooSetting"/>
        <attr name="settings.instanceOf" stringvalue="org.netbeans.modules.foo.FooSetting"/>
        <!--changes of propertyName1, propertyName2 will be ignored-->
        <attr name="xmlproperties.ignoreChanges" stringvalue="propertyName1, propertyName2"/>
        <!--the setting object will not be stored automatically-->
        <attr name="xmlproperties.preventStoring" boolvalue="true"/>
      </file>
    </folder>
  </folder>

  <!--FooSetting class to XMLPropertiesConvertor mapping (for persistent instances
    created in the runtime)-->
  <folder name="memory">
    <folder name="org">
      <folder name="netbeans">
        <folder name="modules">
          <folder name="foo">
            <!--allows to create .settings file from memory via InstanceDataObject.create-->
            <file name="FooSetting">
              <attr name="settings.providerPath"
                stringvalue="xml/lookups/NetBeans_org_netbeans_modules_foo/DTD_FooSetting_1_0.instance"/>
            </file>
          </folder>
        </folder>
      </folder>
    </folder>
  </folder>
</folder>

<!--And here is the FooSetting data registration containing default values-->
<folder name="Services">
  <!--the .settings data file registration-->
  <file name="org-netbeans-modules-foo-FooSetting.settings" url="FooSetting.xml">
    <!-- SystemFileSystem.localizedName and SystemFileSystem.icon as usual -->
  </file>
</folder>
FooSetting.xml containing the persisted setting object:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE properties
 PUBLIC "-//NetBeans org.netbeans.modules.foo//DTD FooSetting 1.0//EN"
 "http://www.netbeans.org/dtds/properties-1_0.dtd"
<properties>
    <property name="property1Name" value="xxx"/>
    <property name="property2Name" value="xxx"/>
</properties>
FooSetting class would look like:
public final class FooSetting {
    private final static String PROP_PROPERTY1NAME = "property1Name"; //NOI18N
    ...
    public FooSetting() {...}
    //  property change event support
    public void addPropertyChangeListener(java.beans.PropertyChangeListener l) {...}
    public void removePropertyChangeListener(java.beans.PropertyChangeListener l) {...}
    // getters/setters
    public String getPropery1() {...}
    public void setProperty1(Object property1) {
        ...
        // fire a property change event
    }
    // readProperties/writeProperties called by XMLPropertiesConvertor
    private void readProperties(java.util.Properties p) {
        property1 = p.getProperty(PROP_PROPERTY1NAME);
        ...
    }
    private void writeProperties(java.util.Properties p) {
        p.setProperty(PROP_PROPERTY1NAME, property1);
        ...
    }
    ...
}


Skip navigation links
org.netbeans.modules.settings/1 1.60