Appendix B. Extensible XML authoring

B.1. Introduction

This section is devoted to detailing how you would go about writing your own custom XML object definition parsers and integrating such parsers into the Spring Actionscript IoC container.

To facilitate the authoring of configuration files using a schema-aware XML editor, Spring Actionscript's extensible XML configuration mechanism is based on XML Schema. If you are not familiar with Spring Actionscript's current XML configuration extensions that come with the standard Spring Actionscript distribution, please first read the appendix entitled "Appendix A, XML Schema-based configuration."

Creating new XML configuration extensions can be done by following these (relatively) simple steps:

What follows is a description of each of these steps. For the example, we will create an XML extension (a custom XML element) that allows us to configure objects of the type DateFormatter (from the Flex mx.formatters.DateFormatter package) in an easy manner. When we are done, we will be able to define object definitions of type DateFormatter like this:

<mydatens:ddateformatter id="dateFormat" 
    format-string="MM/DD/YYYY"/>

(Don't worry about the fact that this example is very simple; much more detailed examples follow afterwards. The intent in this first simple example is to walk you through the basic steps involved.)

B.2. Authoring the schema

Creating an XML configuration extension for use with Spring Actionscript's IoC container starts with authoring an XML Schema to describe the extension. What follows is the schema we'll use to configure Date objects.

<xsd:schema xmlns="http://www.springactionscript.org/schema/mydatens"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   targetNamespace="http://www.springactionscript.org/schema/mydatens"
   elementFormDefault="qualified">

 <xsd:import namespace="http://www.springactionscript.org/schema/objects"/>
 
 <xsd:element name="dateformatter">
  <xsd:complexType>
   <xsd:complexContent>
      <xsd:extension base="objects:objectType">
         <xsd:attribute name="format-string" type="xsd:string" use="required"/>
      </xsd:extension>
   </xsd:complexContent>
  </xsd:complexType>
 </xsd:element>

</xsd:schema>

(The emphasized line contains an extension base for all tags that will are an object (meaning they have an id attribute, class attribute, etc). We are able to use this attribute because we imported the Spring Actionscript-provided 'objects' namespace.)

The above schema will be used to configure Date objects, directly in an XML application context file using the <mydatens:dateformatter/> element.

<mydatens:dateformatter id="dateFormatter" 
    format-string="MM/DD/YYYY"/>

Note that after we've created the infrastructure classes, the above snippet of XML will essentially be exactly the same as the following XML snippet. In other words, we're just creating an object in the container, identified by the name 'dateFormat' of type DateFormatter, with a couple of properties set.

<object id="dateFormatter" class="mx.formatters.DateFormatter">
    <property name="formatString" value="MM/DD/YYYY"/>
</object>

Note

The schema-based approach to creating configuration format allows for tight integration with an IDE that has a schema-aware XML editor. Using a properly authored schema, you can use auto completion to have a user choose between several configuration options defined in the enumeration.

B.3. Coding an INamespaceHandler implementation

In addition to the schema, we need an INamespaceHandler Interface to be implemented when creating a custom namespace handler. that will parse all elements of this specific namespace Spring Actionscript encounters while parsing configuration files. The INamespaceHandler Interface to be implemented when creating a custom namespace handler. should in our case take care of the parsing of the mydatens:dateformatter element.

The INamespaceHandler Interface to be implemented when creating a custom namespace handler. interface is pretty simple in that it features just two methods:

public interface INamespaceHandler {

 /**
  * Return the namespace supported by this namespace handler.
  */
 function getNamespace():Namespace;

 /**
  * Parses the given node and returns the resulting object definition.
  * 
  * @param node the xml node to parse
  * @param context the root xml parser
  */
 function parse(node:XML, context:XMLObjectDefinitionsParser):IObjectDefinition;

}

Although it is perfectly possible to code your own INamespaceHandler Interface to be implemented when creating a custom namespace handler. for the entire namespace (and hence provide code that parses each and every element in the namespace), it is often the case that each top-level XML element in a Spring Actionscript XML configuration file results in a single object definition (as in our case, where a single <mydatens:dateformatter/> element results in a single DateFormatter object definition). Spring Actionscript features a number of convenience classes that support this scenario. In this example, we'll make use of the NamespaceHandlerSupport Offers basic support for namespace handlers. class:

package org.springextensions.actionscript.ioc.factory.xml {

 public class MyNamespaceHandler extends NamespaceHandlerSupport {

  public function MyNamespaceHandler() {
   super(new Namespace("http://www.springactionscript.org/schema/mydatens"));

   registerObjectDefinitionParser("dateformatter", new DateFormatterNodeParser());
  }

 }
}

The observant reader will notice that there isn't actually a whole lot of parsing logic in this class. Indeed... the NamespaceHandlerSupport Offers basic support for namespace handlers. class has a built in notion of delegation. It supports the registration of any number of IObjectDefinitionParser Interface to be implemented by custom object definition parsers, used in a namespace handler. instances, to which it will delegate to when it needs to parse an element in it's namespace. This clean separation of concerns allows an INamespaceHandler Interface to be implemented when creating a custom namespace handler. to handle the orchestration of the parsing of all of the custom elements in it's namespace, while delegating to IObjectDefinitionParsers Interface to be implemented by custom object definition parsers, used in a namespace handler. to do the grunt work of the XML parsing; this means that each IObjectDefinitionParser Interface to be implemented by custom object definition parsers, used in a namespace handler. will contain just the logic for parsing a single custom element, as we can see in the next step.

B.4. Coding an IObjectDefinitionParser implementation

An IObjectDefinitionParser Interface to be implemented by custom object definition parsers, used in a namespace handler. will be used if the INamespaceHandler Interface to be implemented when creating a custom namespace handler. encounters an XML element of the type that has been mapped to the specific object definition parser (which is 'dateformatter' in this case). In other words, the IObjectDefinitionParser Interface to be implemented by custom object definition parsers, used in a namespace handler. is responsible for parsing one distinct top-level XML element defined in the schema. In the parser, we'll have access to the XML element (and thus it's sub elements too) so that we can parse our custom XML content, as can be seen in the following example:

package org.springextensions.actionscript.ioc.factory.xml {
 import mx.formatters.DateFormatter;

 import org.springextensions.actionscript.ioc.IObjectDefinition;
 import org.springextensions.actionscript.ioc.factory.support.ObjectDefinitionBuilder;
 import org.springextensions.actionscript.ioc.factory.xml.parser.support.ParsingUtils;
 import org.springextensions.actionscript.ioc.factory.xml.parser.support.XMLObjectDefinitionsParser;

 public class DateFormatterNodeParser extends AbstractObjectDefinitionParser {

  public function DateFormatterNodeParser() {
   super();
  }

  override protected function parseInternal(node:XML, context:XMLObjectDefinitionsParser):IObjectDefinition {
   var result:ObjectDefinitionBuilder = ObjectDefinitionBuilder.objectDefinitionForClass(DateFormatter);

   ParsingUtils.mapProperties(result.objectDefinition, node, "format-string");

   return result.objectDefinition;

  }

 }
}

In this simple case, this is all that we need to do. The creation of our single IObjectDefinition Represents an object definition. is handled by the AbstractObjectDefinitionParser Abstract implementation of IObjectDefinitionParser that offers templating for parsing and registering an object definition. superclass, as is the extraction and setting of the object definition's unique identifier.

B.5. Registering the handler

The coding is finished! All that remains to be done is to somehow make the Spring Actionscript XML parsing infrastructure aware of our custom element. For this particular task you can simply call the addNamespaceHandler() Adds a namespace handler to the parser of this application context. method on the application context instance, passing it an instance of your newly created INamespaceHandler Interface to be implemented when creating a custom namespace handler..

applicationContext.addNamespaceHandler(new MyNamespaceHandler());

And that's it, if you make sure you've declared your own namespace properly in the XML configuration, from now on the Spring Actionscript container will be able to parse and create your object definitions.

Note

Obviously you must invoke the addNameSpaceHandler() method before you invoke the load() method.

B.6. Code generator

Spring Actionscript offers a small AIR application (whose source can also be found in the 'samples' section of the SVN repository). This application can load and analyze a .swc file, extract class information and based on this generate a namespace, a NamespaceHandlerSupport Offers basic support for namespace handlers. subclass, a list of IObjectDefinitionsParsers Interface to be implemented by custom object definition parsers, used in a namespace handler. and a schema file.

Obviously this code generator will not be suitable for more complex implementations, but it can at least provide you with some boiler plate code and give you a better insight in the workings and dependencies of the different classes.

You can download the AIR installer by clicking this link.