Convertor
,
Creating a convertor means creating a new class which subclasses
the abstract Convertor
class.
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.
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>
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.
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:
DOMConvertor
is available read/write operation is delegated to
DOMConvertor.readElement
or DOMConvertor.writeElement
which are
supposed to be rewritten by subclasses.
DOMConvertor
is not available a plain Convertor
is used and its output
is encapsulated in a CDATA block inside the domconvertor
element.
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.
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.
FileSystem fs = Repository.getDefault().getDefaultFileSystem(); FileObject fo = fs.findResource("YourFolder/YourSetting.settings"); DataObject dobj = DataObject.find(fo); InstanceCookie ic = (InstanceCookie) dobj.getCookie(InstanceCookie.class); Object setting = ic.instanceCreate();
settings.instanceClass
and settings.instanceOf
. If an old class
is referenced inside the format use META-INF/netbeans/translate.names
file.
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.
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.
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_org_netbeans_modules_foo_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_org_netbeans_modules_foo_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); ... } ... }