This is intended to satisfy a number of requirements:
- No XML-specific code in the objects being loaded/stored.
- Separation at the package level between load/store code and the configured objects
- Customizable storage of complex objects
- Control over the order in which complex objects are recreated during load.
To do this:
- The application registers relevant objects with the ConfigXmlManager class. These objects are usually the higher-level managers; since code has to be written for the actual class of these objects, you should design to have a minimal number of them. For example, a LayoutManager object of the specific LocoNetLayoutManager class might be registered.
- The ConfigXmlManager checks that an adapter class for that object exists. The adapter class for an object of package and class "p.c" is located at runtime by searching for a class called "p.configurexml.cXml", and must implement jmri.configurexml.Adapter.
- When a store operation is required, the ConfigXmlManager object iterates over the set of registered objects. For each, it creates an adapter object and requests a store operation, which returns a JDOM Element which is added to the output XML.
- When a load operation is required, the input XML file is iterated through. For each top-level Element, an adapter object of the described type is created and passed the Element. Note that the adapter should register the objects it creates!
We choose to put the adapters in a separate package to encourage the reduction of coupling, and make it clear that they are separate (you can get a source tree without them easily, for example). But this means that package-level member variables are not automatically visible. For one class, you can get around this by subclassing. We'll have to see whether this is a real problem or not.
On read, the right class has to be found to handle any given top-level XML element. We do this in an ugly fashion by putting the class name in the "class" attribute. This prevents the XML output from being portable from program to program, an unfortunate loss, but for the time being it seems a reasonable compromise.
The various types do still have descriptive element names (e.g. "turnouts") to help the reader and so that we can have an understandable schema.
The order of loading is the order in the XML file, as written. This constrains the write order: It has to make sure that the reading process works, in that objects are created before others need to reference them.
There are four levels of information, a coarse measure to ensure that prerequisites are present during a load:
- Pref - program-level preferences
- Config - NamedBeanManagers
- Tool - not used
- User - panels
Schema managementValidation is via XML Schema. (Use of DTDs ended in the 2.9 development series.)
Schemas for these files are found in the xml/schema directory.
A rudimentary version control system is provided by schema file naming, i.e.
- layout.xsd - the original, not to be changed any more.
- layout-2-9-6.xsd - manually created when the first non-backward-compatible XML content change was made after the release of JMRI 2.9.5, this accumulates changes until some later non-backward-compatible change is made.
- layout-4-19-2.xsd - a significant update in structure and content.
- Copy the existing schema to one with the next version name (e.g. while building the 3.1.2 release, copy layout-2-9-6.xsd to layout-config-3-1-2.xsd) and commit to Git.
- Update the comment on the previous file.
- Update the schemaVersion value in the
Related DocumentationFor overviews, tutorials, examples, guides, and tool documentation, please see:
Class Summary Class Description AbstractXmlAdapterAbstract class to provide basic error handling for XmlAdapter AbstractXmlAdapter.EnumIO<T extends Enum<T>>Base for support of Enum load/store to XML files. AbstractXmlAdapter.EnumIoMapped<T extends Enum<T>>Support for Enum I/O to XML using explicit mapping. AbstractXmlAdapter.EnumIoNames<T extends Enum<T>>Support for Enum I/O to XML using the enum's element names. AbstractXmlAdapter.EnumIoNamesNumbers<T extends Enum<T>>Support for Enum I/O to XML using the enum's element names; for backward compatibility, it will also accept ordinal numbers when reading. AbstractXmlAdapter.EnumIoOrdinals<T extends Enum<T>>Support for Enum I/O to XML using the enum's ordinal numbers in String form. BlockManagerXmlPersistency implementation for BlockManager persistence. Bundle ClassMigrationManagerGet class migrations for the
ConfigureXmlStartupActionFactoryFactory for XML file loading startup actions. ConfigXmlManagerProvides the mechanisms for storing an entire layout configuration to XML. DccLocoAddressXmlHandle XML configuration for DccLocoAddress objects. DefaultClassMigrationDefault class migrations for the
DefaultJavaBeanConfigXMLProvides services for storing Java Beans to XML using reflection. ErrorHandlerDefault operation for reporting errors while loading. ErrorMemoMemo class to remember errors encountered during loading LoadStoreBaseActionBase implementation for the load and store actions. LoadXmlConfigActionLoad configuration information from an XML file. LoadXmlUserActionLoad configuration information from an XML file. LocoAddressXmlHandle XML configuration for LocoAddress objects. ScaleConfigXMLLoad and store scale xml data files. SectionManagerXmlProvides the functionality for persistence of a SectionManager. StoreMenuCreate a "Save" menu item containing actions for storing various data (subsets). StoreXmlAllActionStore the entire JMRI status in an XML file. StoreXmlConfigActionStore the JMRI configuration information as XML. StoreXmlUserActionStore the JMRI user-level information as XML. TransitManagerXmlProvides the functionality for configuring a TransitManager. TurnoutOperationManagerXml
Exception Summary Exception Description JmriConfigureXmlExceptionBase for JMRI-specific ConfigureXml exceptions.