A specialized handler for a specific type in an xml schema.
Bindings have the following responsibilities.
- Parsing components from an instance document (elements and attributes)
into model objects
- Encoding model objects as xml components
- Sorting themselves in case multiple bindings target the same type
Type Binding
Binding objects correspond to xml schema types. A binding declares
it target type by advertising the qualified name of the type it binds to.
For instance, the following strategy binds itself to type xsd:string.
class XSDStringStrategy {
QName getTarget() {
return new QName("http://www.w3.org/2001/XMLSchema","string");
}
...
The upshot is that whenever an element or attribute is encountered in an
instance document that is of type xsd:string, this binding will be used to
turn the string into an object representation.
And on the other side of coin, when an instanceof String is encountered when
serializing an object model, the binding will be used to encode the string as
xml.
Inheritance
XML Schema supports Inheritance. As a concrete example, consider the simple
xml schema simple types decimal and integer.
<xs:simpleType name="decimal" id="decimal">
<xs:annotation>
<xs:appinfo>
<hfp:hasFacet name="totalDigits"/>
<hfp:hasFacet name="fractionDigits"/>
<hfp:hasFacet name="pattern"/>
<hfp:hasFacet name="whiteSpace"/>
<hfp:hasFacet name="enumeration"/>
<hfp:hasFacet name="maxInclusive"/>
<hfp:hasFacet name="maxExclusive"/>
<hfp:hasFacet name="minInclusive"/>
<hfp:hasFacet name="minExclusive"/>
<hfp:hasProperty name="ordered" value="total"/>
<hfp:hasProperty name="bounded" value="false"/>
<hfp:hasProperty name="cardinality" value="countably infinite"/>
<hfp:hasProperty name="numeric" value="true"/>
</xs:appinfo>
<xs:documentation source="http://www.w3.org/TR/xmlschema-2/#decimal"/>
</xs:annotation>
<xs:restriction base="xs:anySimpleType">
<xs:whiteSpace fixed="true" value="collapse" id="decimal.whiteSpace"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="integer" id="integer">
<xs:annotation>
<xs:documentation source="http://www.w3.org/TR/xmlschema-2/#integer"/>
</xs:annotation>
<xs:restriction base="xs:decimal">
<xs:fractionDigits fixed="true" value="0" id="integer.fractionDigits"/>
<xs:pattern value="[\-+]?[0-9]+"/>
</xs:restriction>
</xs:simpleType>
The above types define an inheiretance hierarcy. To model this relationship
among the corresponding binding objects, an execution mode must be
declared by each binding. The execution mode specifies wether a binding
should be executed before, after, or totally override the binding for a
direct parent.
class DecimalBinding implements Binding {
...
int getExecutionMode() {
return OVERRIDE;
}
...
}
class IntegerBinding implemnts Binding {
...
int getExecutionMode() {
return AFTER;
}
...
}
In the above example, the decimal bidning declares its execution mode to
be override. This means that no bindings for any of the base types of
decimal will be executed. The integer binding declares its execution mode
as AFTER. This means that the integer binding will be executed after the
decimal strategy.
Context
Bindings are executed within a particular context or container. A binding
can use its context to obtain objects that it depends on to perform a parse
or encoding. A binding can declare a dependency by simply adding it to its
constructor.
This is known as
Constructor Injection.
When the binding is created it is injected with all dependencies. The context is
responsible for satisfying the dependencies of the binding. As an example
consider the following complex type defintion.
<xsd:complexType name="collection">
<xsd:sequence>
<xsd:element name="item" type="xsd:any" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
The associated binding must turn instances of this type into objects of
type
java.util.Collection . However, the question remains what
concrete subclass of Collection to use. And perhaps we need this type to
vary in different situations. The solution is to take the decision out of
the hands of this binding, and into the hands of someone else. To acheive
this the collection binding adds a dependency of type Collection.
class CollectionStrategy implements ComplexBinding {
Collection collection;
CollectionStrategy(Collection collection) {
this.collection = collection;
}
QName getTarget() {
return new QName("http://org/geotools/","collection");
}
int getExecutionMode() {
return OVERRIDE;
}
Object parse(Element instance, Node[] children, Node[] atts, Object value)
throws Exception {
for (int i = 0; i < children.length; i++) {
collection.add(children[i].getValue());
}
return collection;
}
}
Conflict resolution
In some cases multiple bindings are targetting the same java class. This happens, for example,
in GML3 where
MultiPolygon is associated to two different elements, gml:MultiPolygon
and gml:MultiSurface (the former being deprecated and kept for backwards compatibility).
In such occasions, binding implementations must implement the
Comparable interface,
in case of doubt the bindings associated to a specific class will be sorted and the first
element in the resulting
List will be used.
author: Justin Deoliveira,Refractions Research Inc.,jdeolive@refractions.net |